{-# LANGUAGE OverloadedStrings #-}

module Language.Sexp.Encode
  ( encode
  ) where

import Data.List (intersperse)
import Data.Monoid
import Data.Scientific
import Data.Text.Encoding (encodeUtf8)
import Data.ByteString.Lazy (ByteString)
import Data.ByteString.Lazy.Builder.ASCII

import Language.Sexp.Types

bAtom :: Atom -> Builder
bAtom (AtomBool a)    = char8 '#' <> if a then char8 't' else char8 'f'
bAtom (AtomInt a)     = integerDec a
bAtom (AtomReal a)    = string8 . formatScientific Generic Nothing $ a
bAtom (AtomString a)  = stringUtf8 (show a)
bAtom (AtomSymbol a)  = byteString (encodeUtf8 a)
bAtom (AtomKeyword a) = char8 ':' <> byteString (encodeUtf8 (unKw a))

sep :: [Builder] -> Builder
sep = mconcat . intersperse (char8 ' ')

bSexp :: Sexp -> Builder
bSexp (Atom   _ a)  = bAtom a
bSexp (List   _ ss) = char8 '(' <> sep (map bSexp ss) <> char8 ')'
bSexp (Vector _ ss) = char8 '[' <> sep (map bSexp ss) <> char8 ']'
bSexp (Quoted _ a)  = char8 '\'' <> bSexp a

-- | Quickly encode Sexp to non-indented ByteString
encode :: Sexp -> ByteString
encode = toLazyByteString . bSexp