jsonnet-0.1.0.0: src/Language/Jsonnet/Pretty.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Language.Jsonnet.Pretty where
import qualified Data.Aeson as JSON
import qualified Data.Aeson.Text as JSON (encodeToLazyText)
import qualified Data.HashMap.Lazy as H
import Data.List (sortOn)
import Data.Scientific (Scientific (..))
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Lazy as LT
import Data.Text.Lazy.Builder
import Data.Text.Lazy.Builder.Scientific (scientificBuilder)
import qualified Data.Vector as V
import GHC.IO.Exception (IOException (..))
import Language.Jsonnet.Common
import Language.Jsonnet.Error
import Language.Jsonnet.Parser.SrcSpan
import Text.Megaparsec.Error (errorBundlePretty)
import Text.Megaparsec.Pos
import Text.PrettyPrint.ANSI.Leijen hiding (encloseSep, (<$>))
import Unbound.Generics.LocallyNameless (Name, name2String)
instance Pretty (Name a) where
pretty v = pretty (name2String v)
instance Pretty Text where
pretty v = pretty (T.unpack v)
ppNumber s
| e < 0 || e > 1024 =
text $
LT.unpack $
toLazyText $
scientificBuilder s
| otherwise = integer (coefficient s * 10 ^ e)
where
e = base10Exponent s
ppJson :: Int -> JSON.Value -> Doc
ppJson i =
\case
JSON.Null -> text "null"
JSON.Number n -> ppNumber n
JSON.Bool True -> text "true"
JSON.Bool False -> text "false"
JSON.String s -> ppString s
JSON.Array a -> ppArray a
JSON.Object o -> ppObject o
where
encloseSep l r s ds = case ds of
[] -> l <> r
_ -> l <$$> indent i (vcat $ punctuate s ds) <$$> r
ppObject o = encloseSep lbrace rbrace comma xs
where
prop (k, v) = ppString k <> colon <+> ppJson i v
xs = map prop (sortOn fst $ H.toList o)
ppArray a = encloseSep lbracket rbracket comma xs
where
xs = map (ppJson i) (V.toList a)
ppString = text . LT.unpack . JSON.encodeToLazyText
instance Pretty JSON.Value where
pretty = ppJson 4
instance Pretty SrcSpan where
pretty SrcSpan {spanBegin, spanEnd} =
text (sourceName spanBegin)
<> colon
<> lc spanBegin spanEnd
where
lc (SourcePos _ lb cb) (SourcePos _ le ce)
| lb == le =
int (unPos lb) <> colon
<> int (unPos cb)
<> dash
<> int (unPos ce)
| otherwise =
int (unPos lb) <> colon <> int (unPos cb) <> dash
<> int (unPos le)
<> colon
<> int (unPos ce)
dash = char '-'
instance Pretty ParserError where
pretty (ParseError e) = pretty (errorBundlePretty e)
pretty (ImportError (IOError _ _ _ desc _ f) sp) =
text "Parse error:"
<+> pretty f
<+> parens (text desc)
<$$> indent 4 (pretty sp)
instance Pretty CheckError where
pretty =
\case
DuplicateParam e ->
text "duplicate parameter"
<+> squotes (text e)
DuplicateBinding e ->
text "duplicate local var"
<+> squotes (text e)
PosAfterNamedParam ->
text "positional after named argument"
instance Pretty EvalError where
pretty =
\case
TypeMismatch {..} ->
text "type mismatch:"
<+> text "expected"
<+> text (T.unpack expected)
<+> text "but got"
<+> text (T.unpack actual)
InvalidKey k ->
text "invalid key:"
<+> k
InvalidIndex k ->
text "invalid index:"
<+> k
NoSuchKey k ->
text "no such key:"
<+> k
IndexOutOfBounds i ->
text "index out of bounds:"
<+> ppNumber i
DivByZero ->
text "divide by zero exception"
VarNotFound v ->
text "variable"
<+> squotes (text $ show v)
<+> text "is not defined"
AssertionFailed e ->
text "assertion failed:" <+> e
StdError e -> e
RuntimeError e -> e
ParamNotBound s ->
text "parameter not bound:"
<+> text (show s)
BadParam s ->
text "function has no parameter"
<+> squotes s
ManifestError e ->
text "manifest error:"
<+> e
TooManyArgs n ->
text "too many args, function has"
<+> int n
<+> "parameter(s)"
instance Pretty (StackFrame a) where
pretty StackFrame {..} =
pretty span <+> pretty name'
where
name' = (<+>) (text "function") . angles . pretty <$> name
instance Pretty (Backtrace a) where
pretty (Backtrace xs) = vcat $ pretty <$> xs
instance Pretty Error where
pretty =
\case
EvalError e bt ->
text "Runtime error:"
<+> pretty e
<$$> indent 2 (pretty bt)
ParserError e -> pretty e
CheckError e sp ->
text "Static error:"
<+> pretty e
<$$> indent 2 (pretty sp)