--- /dev/null
+implementation module format
+
+import StdEnv
+
+import Data.Func
+import Data.Either
+import Data.Error
+import Data.Functor
+import Data.List
+import Data.Maybe
+import Data.GenEq
+import Control.Applicative
+import Control.Monad
+import Text => qualified join
+import Text.Parsers.Simple.ParserCombinators
+
+:: FormatString :== [Format]
+:: Format
+ = FMTLiteral String
+ | FMTApply FormatFun [Format]
+
+:: FormatFun
+ = FFTag
+
+derive gEq Format, FormatFun
+instance == FormatString where == x y = x === y
+
+format :: [(String, String)] FormatString -> MaybeErrorString String
+format tags f = concat <$> mapM fmt f
+where
+ fmt :: Format -> MaybeErrorString String
+ fmt (FMTLiteral s) = pure s
+ fmt (FMTApply fun args) = case (fun, args) of
+ (FFTag, [tag,alt])
+ = flip fromMaybe o flip lookup tags <$> fmt tag <*> fmt alt
+ (FFTag, _) = Error "`tag` must have exactly two arguments"
+ _ = Error "Unimplemented function"
+
+parseFormat :: [Char] -> MaybeErrorString FormatString
+parseFormat s = either (Error o concat) Ok (parse (some pFormat) s)
+
+pFormat :: Parser Char Format
+pFormat
+ = FMTApply <$ pToken '$' <*> pFormatFun <* pToken '{' <*> pSepBy pFormat pComma <* pToken '}'
+ <|> FMTLiteral <$> toString <$> some (
+ pToken '\\' *> pSatisfy (\x->isMember x ['${},'])
+ <|> pSatisfy (\x->not (isMember x ['$[],'])))
+
+
+pFormatFun :: Parser Char FormatFun
+pFormatFun = FFTag <$ pList (map pToken ['tag'])