packages feed

symantic-xml-1.0.0.20190223: Symantic/RNC/Write.hs

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE OverloadedStrings #-}
module Symantic.RNC.Write
 ( module Symantic.RNC.Write
 , module Symantic.RNC.Write.Fixity
 , module Symantic.RNC.Write.Namespaces
 ) where

import Control.Applicative (Applicative(..), Alternative(..))
import Control.Monad
import Data.Bool
import Data.Function (($), (.), id)
import Data.Functor ((<$>))
import Data.Functor.Compose (Compose(..))
import Data.Semigroup (Semigroup(..))
import Data.String (IsString(..))
import Text.Show (Show(..))
import qualified Data.HashMap.Strict as HM
import qualified Data.List as List
import qualified Data.Text.Lazy as TL
-- import qualified Data.Text.Lazy.Builder as TLB

import Symantic.RNC.Sym
import Symantic.RNC.Write.Fixity
import Symantic.RNC.Write.Namespaces
import qualified Symantic.XML as XML

-- | Get textual rendition of given 'RuleWriter'.
writeRNC :: [NS a] -> [Writer a] -> TL.Text
writeRNC ns ws =
	let namespaces@XML.Namespaces{..} = runNS ns in
	TL.unlines $ List.concat
	 [ [ "default namespace = \""<>XML.unNamespace namespaces_default<>"\""
		 | not $ TL.null $ XML.unNamespace namespaces_default
		 ]
	 , [ "namespace "<>p<>" = \""<>n<>"\""
		 | (XML.Namespace n, XML.NCName p) <- HM.toList namespaces_prefixes
		 ]
	 , runWriter namespaces <$> ws
	 ]

-- * Type 'Writer'
newtype Writer a
 = Writer { unWriter :: XML.Namespaces XML.NCName ->
                        RuleMode ->
                        (Infix, Side) ->
                        Pair -> TL.Text }

-- | Get textual rendition of given 'Writer'.
runWriter :: XML.Namespaces XML.NCName -> Writer a -> TL.Text
runWriter ns (Writer w) =
	w ns RuleMode_Def (infixN0, SideL) pairParen

coerceWriter :: Writer a -> Writer b
coerceWriter = Writer . unWriter
{-# INLINE coerceWriter #-}

{-
instance Show (Writer a) where
	show = TL.unpack . runWriter
-}
instance Functor Writer where
	fmap _f (Writer x) = Writer x
instance Applicative Writer where
	pure _ = writeText $ "\"\""
	Writer f <*> Writer x = Writer $ \ns rm po pp ->
		pairIfNeeded pp po op $
		TL.intercalate ", " $
		List.filter (not . TL.null) $
		 [ f ns rm (op, SideL) pairParen
		 , x ns rm (op, SideR) pairParen ]
		where op = infixB SideL 2
instance Alternative Writer where
	empty = writeText "empty"
	Writer wl <|> Writer wr = Writer $ \ns rm po pp ->
		pairIfNeeded pp po op $
		wl ns rm (op, SideL) pairParen <> " | " <> wr ns rm (op, SideR) pairParen
		where op = infixB SideL 2
	many (Writer w) = Writer $ \ns rm po pp ->
		pairIfNeeded pp po op $
		w ns rm (op, SideL) pairParen <> "*"
		where op = infixN 9
	some (Writer w) = Writer $ \ns rm po pp ->
		pairIfNeeded pp po op $
		w ns rm (op, SideL) pairParen <> "+"
		where op = infixN 9
instance Sym_Rule Writer where
	rule n wr@(Writer w) = Writer $ \ns rm po pp ->
		case rm of
		 RuleMode_Ref ->
			pairIfNeeded pp po op $
			fromString n
			where op = infixN 10
		 RuleMode_Body -> w ns RuleMode_Ref po pp
		 RuleMode_Def ->
			TL.intercalate " "
			 [ fromString n
			 , "="
			 , unWriter (rule n wr) ns RuleMode_Body (infixN0, SideR) pp
			 ]
	arg n =
		Writer $ \_ns rm _po _pp ->
			case rm of
			 RuleMode_Ref  -> fromString n
			 RuleMode_Body -> ""
			 RuleMode_Def  -> ""
type instance Permutation Writer = Compose [] Writer
instance Sym_Permutation Writer where
	runPermutation (Compose []) = writeText "empty"
	runPermutation (Compose [Writer w]) = Writer w
	runPermutation (Compose l@(_:_)) = Writer $ \ns rm po pp ->
		pairIfNeeded pp po op $
		TL.intercalate " & " $
		List.filter (not . TL.null) $
		(unWriter <$> l) <*> pure ns <*> pure rm <*> pure (op, SideL) <*> pure pairParen
		where op = infixB SideL 1
	toPermutation = Compose . pure
	toPermutationWithDefault _ = Compose . pure
instance Sym_RNC Writer where
	namespace _p _n = writeText ""
	element n (Writer w) = Writer $ \ns rm po pp ->
		pairIfNeeded pp po op $
		"element "<>TL.pack (show $ XML.prefixifyQName ns n)
		 <>" "<>w ns rm (op,SideR) pairBrace
		where op = infixN 10
	anyElem (XML.Namespace n) f = Writer $ \ns rm po pp ->
		pairIfNeeded pp po op $
		(if TL.null n then "" else n<>":") <>
		"* "<>w ns rm (op,SideR) pairBrace
		where
		op = infixN 0
		Writer w = f ""
	attribute n (Writer w) = Writer $ \ns rm po pp ->
		pairIfNeeded pp po op $
		"attribute "<>TL.pack (show $ XML.prefixifyQName ns n)
		 <>" "<>w ns rm (op,SideR) pairBrace
		where op = infixN 10
	try         = id
	fail        = writeText "fail"
	escapedText = writeText "text"
	text        = writeText "text"
	any         = writeText "any"
	choice []   = writeText "empty"
	choice [w]  = w
	choice l@(_:_) = Writer $ \ns rm po pp ->
		pairIfNeeded pp po op $
		TL.intercalate " | " $
		(unWriter <$> l) <*> pure ns <*> pure rm <*> pure (op, SideL) <*> pure pairParen
		where op = infixB SideL 2
	option _x (Writer w) = Writer $ \ns rm po pp ->
		pairIfNeeded pp po op $
		w ns rm (op, SideL) pairParen <> "?"
		where op = infixN 9
	optional (Writer w) = Writer $ \ns rm po pp ->
		pairIfNeeded pp po op $
		w ns rm (op, SideL) pairParen <> "?"
		where op = infixN 9
	manySeq = coerceWriter . many
	someSeq = coerceWriter . some

-- | 'Writer' returns a constant rendition.
writeText :: TL.Text -> Writer a
writeText t = Writer $ \_ns _rm po pp ->
	pairIfNeeded pp po op t
	where op = infixN 10