twine (empty) → 0.1.1
raw patch · 12 files changed
+723/−0 lines, 12 filesdep +basedep +bytestringdep +containerssetup-changed
Dependencies added: base, bytestring, containers, filepath, mtl, parsec
Files
- LICENSE +8/−0
- Setup.lhs +3/−0
- src/Text/Twine.hs +18/−0
- src/Text/Twine/Interpreter.hs +204/−0
- src/Text/Twine/Interpreter/Builtins.hs +15/−0
- src/Text/Twine/Interpreter/Context.hs +68/−0
- src/Text/Twine/Interpreter/ContextWriter.hs +33/−0
- src/Text/Twine/Interpreter/FancyContext.hs +75/−0
- src/Text/Twine/Interpreter/Types.hs +61/−0
- src/Text/Twine/Parser.hs +173/−0
- src/Text/Twine/Parser/Types.hs +30/−0
- twine.cabal +35/−0
+ LICENSE view
@@ -0,0 +1,8 @@+Copyright (c) 2009, James Sanders+All rights reserved.++Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.lhs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ src/Text/Twine.hs view
@@ -0,0 +1,18 @@+module Text.Twine (module Text.Twine.Parser+ ,module Text.Twine.Interpreter+ ,module Text.Twine.Interpreter.Context+ ,module Text.Twine.Interpreter.FancyContext+ ,module Text.Twine.Interpreter.ContextWriter+ ,evalTemplate)+where++import Text.Twine.Parser+import Text.Twine.Interpreter.Types+import Text.Twine.Interpreter.Context+import Text.Twine.Interpreter.FancyContext+import Text.Twine.Interpreter.ContextWriter+import Text.Twine.Interpreter++evalTemplate template context = do + templ <- loadTemplateFromFile template+ runEval templ context
+ src/Text/Twine/Interpreter.hs view
@@ -0,0 +1,204 @@+{-# LANGUAGE NoMonomorphismRestriction #-}+module Text.Twine.Interpreter (runEval) where++--import Text.Twine++import Control.Monad.State+import Control.Monad.Writer+import Control.Monad.Identity++import Text.Twine.Interpreter.Types+import Text.Twine.Interpreter.Context+import Text.Twine.Interpreter.FancyContext+import Text.Twine.Interpreter.Builtins+import Text.Twine.Parser.Types+import Data.ByteString.Char8 (ByteString,pack,unpack)+import qualified Data.ByteString.Char8 as C+import Debug.Trace+import qualified Data.Map as M++type Stack m a = StateT (ContextState m) (WriterT [String] m) a++-- simpleContext++foldCX :: (Monad m) => [TwineElement m] -> TwineElement m+foldCX = foldl (<+>) emptyContext++-- Context Writer Monad --++mergeCXP (TwineObjectList a) (TwineObjectList b) = TwineObjectList (a ++ b)+mergeCXP (TwineList a) (TwineList b) = TwineList (a ++ b)+mergeCXP (TwineObject a) x = TwineObjectList [a] `mergeCXP` x+mergeCXP x (TwineObject a) = x `mergeCXP` TwineObjectList [a]+mergeCXP a b = error $ "Cannot merge " ++ show a ++ " and " ++ show b+(<+>) = mergeCXP+++runStack run state = runWriterT (runStateT run state)++lift2 f = lift $ lift $ f++debug _ fn = fn +--debug = trace++runEval :: (Monad m, Functor m) => Template -> TwineElement m -> m ByteString+runEval tm cx = do + ((r,log),_) <- runStack (eval' tm) (ContextState cx M.empty)+ debug (show r) $ do+ return $ C.concat r++getCX :: (Monad m) => Stack m (TwineElement m)+getCX = do s <- get + return (getContextState s)++putCX :: (Monad m) => TwineElement m -> Stack m ()+putCX cx = do s <- get+ put $ s { getContextState = cx }++eval' :: (Monad m, Functor m) => [TemplateCode] -> Stack m [ByteString] +eval' = mapM eval++eval :: (Monad m, Functor m) => TemplateCode -> Stack m ByteString+eval (Text x) = return x ++eval (Slot x) = debug ("evaluating slot: " ++ show x) $ do+ ee <- evalExpr x+ st <- case ee of+ (TwineObjectList [x]) -> lift2 $ getString x+ (TwineObject x) -> lift2 $ getString x+ x -> lift2 $ makeString x+ return (C.pack st)+ ++eval (Assign k e) = debug ("evaluating assign " ++ show k ++ " = " ++ show e) $ do + ee <- evalExpr e+ st <- getCX+ putCX (bind [(k,ee)] <+> st)+ return (C.pack "")++eval (Cond e bls) = do+ ee <- evalExpr e+ st <- getCX+ case ee of+ (TwineNull) -> return (C.pack "") + (TwineBool False) -> return (C.pack "")+ _ -> lift2 $ runEval bls st+++eval (Loop e as bls) = do+ ee <- evalExpr e+ case ee of + TwineNull -> return (C.pack "")+ a -> runLoop a+ where runLoop (TwineList ls) = fmap (C.concat) $ mapM inner ls+ + runLoop (TwineObjectList x) = do+ it <- lift2 $ getIterable (head x)+ runLoop (TwineList it)+ + runLoop (TwineObject x) = do+ it <- lift2 $ getIterable x+ runLoop (TwineList it)++ runLoop x = error $ "Not iterable: " ++ show x+ inner v = do cx <- getCX+ lift2 $ runEval bls (bind [(as,v)] <+> cx)++eval x = error $ "Cannot eval: '" ++ (show x) ++ "'"++fromMaybeToContext (Just a) = a+fromMaybeToContext Nothing = TwineNull+++evalExpr :: (Monad m, Functor m) => Expr -> Stack m (TwineElement m)+evalExpr (Func n a) = do + cx <- getCX+ ll <- lift2 $ doLookup n cx+ case ll of + Just (TwineFunction f) -> do + args <- mapM evalExpr a+ lift2 $ f args+ _ -> error $ (C.unpack n) ++ " is not a function. "++ +evalExpr (Var n) = do g <- getCX + r <- lift2 $ doLookup n g+ case r of+ Just a -> return a+ Nothing-> return TwineNull++evalExpr (NumberLiteral n) = return . bind $ n+evalExpr (StringLiteral n) = return . bind $ n++evalExpr acc@(Accessor n expr) = do+ g <- getCX+ accessObjectInContext g acc+ +accessObjectInContext :: (Monad m, Functor m) => TwineElement m -> Expr -> Stack m (TwineElement m)+accessObjectInContext context (Accessor (Var n) expr) = do+ cx <- lift2 $ doLookup' n context+ case cx of+ Nothing -> error "ERROR"+ Just cx' -> accessObjectInContext cx' expr++accessObjectInContext context (Accessor (Func n a) expr) = do+ cx <- lift2 $ doLookup' n context+ case cx of + Nothing -> error "ERROR"+ Just cx' ->+ case cx' of+ TwineFunction f -> do+ args <- mapM evalExpr a+ z <- lift2 $ f args+ accessObjectInContext z expr+ _ -> accessObjectInContext cx' expr++accessObjectInContext context (Var n) = do+ cx <- lift2 $ doLookup' n context+ case cx of+ Just x -> return x+ Nothing -> error "ERROR"+ ++accessObjectInContext context (Func n args) = do+ cx <- lift2 $ doLookup' n context+ case cx of + Nothing -> error "Error"+ Just cx' -> do+ case cx' of+ TwineFunction f -> do + args' <- mapM evalExpr args+ lift2 $ f args'+ _ -> error "Not a callable method"++++doLookup k v = let parts = C.split '.' k+ in aux parts v+ where + aux [] t = return (Just t)+ aux (x:xs) t = do ll <- doLookup' x t+ case ll of+ Just a -> aux xs a+ Nothing -> return Nothing++doLookup' _ (TwineObjectList []) = return Nothing+doLookup' st (TwineObjectList (x:xs)) = do+ let cx = getContext x+ s <- cx st+ case s of+ TwineNull -> doLookup' st (TwineObjectList xs)+ a -> return (Just a)++doLookup' st (TwineBool True) = return (Just $ TwineString $ pack "True")+doLookup' st (TwineBool False) = return Nothing+doLookup' st (TwineObject m) = doLookup' st (TwineObjectList [m])+doLookup' st x = error $ "Context not searchable when looking up '" ++ unpack st ++ "' in " ++ show x++------------------------------------------------------------------------++split :: Char -> [Char] -> [[Char]]+split delim s+ | [] == rest = [token]+ | otherwise = token : split delim (tail rest)+ where (token,rest) = span (/= delim) s
+ src/Text/Twine/Interpreter/Builtins.hs view
@@ -0,0 +1,15 @@+{-# LANGUAGE OverloadedStrings #-}+module Text.Twine.Interpreter.Builtins (builtins) where++import Data.Char+import Data.List+import Text.Twine.Interpreter.Types+import Text.Twine.Interpreter.Context+import Control.Exception (assert)+import qualified Data.ByteString.Char8 as C+import qualified Data.Map as M++myNot [TwineBool b] = return . bind $ not b++builtins :: (Monad m) => M.Map C.ByteString (TwineElement m)+builtins = M.fromList [("not", TwineFunction myNot)]
+ src/Text/Twine/Interpreter/Context.hs view
@@ -0,0 +1,68 @@+{-#LANGUAGE MultiParamTypeClasses+ , TypeSynonymInstances+ , FlexibleInstances+ , FlexibleContexts + , NoMonomorphismRestriction+ , OverlappingInstances+ , OverloadedStrings+ , UndecidableInstances+ , FunctionalDependencies+ #-}++module Text.Twine.Interpreter.Context (emptyContext, TemplateInterface (..)) where++import Data.ByteString.Char8 (ByteString)+import Data.Maybe+import Data.Monoid+import Text.Twine.Interpreter.Types+import qualified Data.ByteString.Char8 as C+import Control.Monad.Writer+import qualified Data.Map as M++class (Monad m) => TemplateInterface m a | a -> m where+ property :: ByteString -> a -> m (TwineElement m)+ makeIterable :: a -> m [TwineElement m]+ makeString :: a -> m String+ bind :: (TemplateInterface m a) => a -> TwineElement m++ property _ _ = return TwineNull+ makeIterable _ = return []+ makeString _ = return ""+ bind a = TwineObject $ Context {+ getContext = (flip property a),+ getIterable = makeIterable a,+ getString = makeString a+ }+++instance (Monad m) => TemplateInterface m (TwineElement m) where+ bind = id+ makeString = return . show++instance (Monad m) => TemplateInterface m ([TwineElement m] -> m (TwineElement m)) where+ bind = TwineFunction++instance (Monad m) => TemplateInterface m EmptyContext ++instance (Monad m, TemplateInterface m a) => TemplateInterface m (Maybe a) where+ bind (Just a) = bind a+ bind Nothing = TwineNull++instance (Monad m) => TemplateInterface m [(ByteString,TwineElement m)] where+ property k = return . fromMaybe TwineNull . lookup k++instance (Monad m) => TemplateInterface m String where+ bind = TwineString . C.pack++instance (Monad m) => TemplateInterface m ByteString where+ bind a = TwineString a++instance (Monad m) => TemplateInterface m Bool where+ bind = TwineBool++instance (Monad m) => TemplateInterface m (M.Map ByteString (TwineElement m)) where+ property k = return . fromMaybe (TwineNull) . M.lookup k++emptyContext :: (Monad m) => TwineElement m+emptyContext = bind EmptyContext+
+ src/Text/Twine/Interpreter/ContextWriter.hs view
@@ -0,0 +1,33 @@+{-#LANGUAGE MultiParamTypeClasses+ , TypeSynonymInstances+ , FlexibleInstances+ , FlexibleContexts + , NoMonomorphismRestriction+ , OverlappingInstances+ , OverloadedStrings+ , UndecidableInstances+ #-}+module Text.Twine.Interpreter.ContextWriter (makeContext, (=:)) where++import Data.ByteString.Char8 (ByteString)+import Data.Maybe+import Data.Monoid+import Text.Twine.Interpreter.Types+import Text.Twine.Interpreter.Context+import Text.Twine.Interpreter.FancyContext+import Text.Twine.Interpreter.Builtins+import Data.ByteString.Char8 (ByteString)+import qualified Data.ByteString.Char8 as C+import Control.Monad.Writer+import Data.Map (Map)+import qualified Data.Map as M++type TwineObjectper m = Map ByteString (TwineElement m)+type ContextWriter m = WriterT (TwineObjectper m) m () ++makeContext :: (Monad m) => ContextWriter m -> m (TwineElement m) +makeContext cw = do+ mp <- execWriterT cw + return $ bind (mp `M.union` builtins)++k =: v = tell $ M.fromList [(C.pack k, bind v)]
+ src/Text/Twine/Interpreter/FancyContext.hs view
@@ -0,0 +1,75 @@+{-#LANGUAGE MultiParamTypeClasses+ , TypeSynonymInstances+ , FlexibleInstances+ , FlexibleContexts + , NoMonomorphismRestriction+ , OverlappingInstances+ , OverloadedStrings+ , UndecidableInstances+ #-}+++module Text.Twine.Interpreter.FancyContext where+import Text.Twine.Interpreter.Types+import Text.Twine.Interpreter.Context++instance (TemplateInterface m a) => TemplateInterface m [a] where+ bind = bind . CXListLike . map bind ++instance (Monad m) => TemplateInterface m (CXListLike m) where+ property "length" = mbind . length . unCXListLike+ property "head" = mbind . head . unCXListLike+ property "tail" = mbind . tail . unCXListLike+ property "init" = mbind . init . unCXListLike+ property "last" = mbind . last . unCXListLike+ property "item" = \x -> return $ TwineFunction (\[n] -> do+ i <- cxToInteger n+ mbind (unCXListLike x !! fromIntegral i))+ + property "take" = \x -> return $ TwineFunction (\[n] -> do+ i <- cxToInteger n+ mbind . take (fromIntegral i) $ unCXListLike x)+ + property "drop" = \x -> return $ TwineFunction (\[n] -> do+ i <- cxToInteger n+ mbind . drop (fromIntegral i) $ unCXListLike x)+ + makeIterable = return . unCXListLike+ makeString = \_-> return "<list>"++------------------------------------------------------------------------++instance (Monad m) => TemplateInterface m Int where+ bind = bind . CXInteger . fromIntegral+ +instance (Monad m) => TemplateInterface m Integer where+ bind = bind . CXInteger++instance (Monad m) => TemplateInterface m CXInteger where+ makeString = return . show . unCXInteger+ property "toInteger" = return . TwineInteger . unCXInteger + property "even?" = mbind . even . unCXInteger+ property "odd?" = mbind . odd . unCXInteger+ property "add" = \a-> return $ TwineFunction (\[n]-> do + i <- cxToInteger n+ mbind $ (unCXInteger a + i)+ )+ property "subtract" = \a-> return $ TwineFunction (\[n]-> do + i <- cxToInteger n+ mbind $ (unCXInteger a - i)+ )++mbind = return . bind+ +signal sig (TwineObject obj) = (getContext obj) sig ++cxToInteger (TwineInteger i) = return i+cxToInteger cm@(TwineObject obj) = do+ v <- signal "toInteger" cm+ case v of+ TwineInteger i -> return i+ TwineNull -> error "expected number but got null value"+ _ -> error ("'" ++ show v ++ "' is not a number")++ +
+ src/Text/Twine/Interpreter/Types.hs view
@@ -0,0 +1,61 @@+{-#LANGUAGE MultiParamTypeClasses+ , TypeSynonymInstances+ , FlexibleInstances+ , UndecidableInstances+ , FlexibleContexts+ , OverlappingInstances+ , OverloadedStrings+ , NoMonomorphismRestriction+ , FunctionalDependencies+ #-}++module Text.Twine.Interpreter.Types where++import qualified Data.ByteString.Char8 as C+import Data.ByteString.Char8 (ByteString,pack)+import Control.Monad.Writer+import Control.Monad.Identity +import Control.Monad.Trans+import qualified Data.Map as M++data TwineElement m = TwineObjectList [Context m]+ | TwineObject (Context m)+ | TwineString ByteString+ | TwineInteger Integer+ | TwineBool Bool+ | TwineNull+ | TwineList [TwineElement m]+ | TwineFunction ([TwineElement m] -> m (TwineElement m))++newtype CXListLike m = CXListLike { unCXListLike :: [TwineElement m] }+newtype CXInteger = CXInteger { unCXInteger :: Integer }++type BuiltinFunc m = [TwineElement m] -> m (TwineElement m)++data ContextState m = ContextState { getContextState :: (TwineElement m)+ , getContextFuns :: M.Map C.ByteString (BuiltinFunc m) }+++instance (Monad m) => Eq (TwineElement m) where+ (TwineString x) == (TwineString y) = x == y+ (TwineList x) == (TwineList y) = x == y+ _ == _ = error "Unable to determine equality."++instance (Monad m) => Show (TwineElement m) where+ show (TwineString x) = C.unpack x+ show (TwineObjectList _) = "((TwineObject))"+ show (TwineObject _) = "((TwineObject))"+ show (TwineList _) = "((TwineList))"+ show (TwineBool x) = show x+ show (TwineNull) = ""+ show (TwineFunction _) = "((TwineFunction))"++data EmptyContext = EmptyContext++data Context m = Context { + getContext :: ByteString -> m (TwineElement m),+ getIterable :: m [TwineElement m],+ getString :: m String+}++
+ src/Text/Twine/Parser.hs view
@@ -0,0 +1,173 @@+{-# LANGUAGE NoMonomorphismRestriction #-}+module Text.Twine.Parser (loadTemplateFromFile, loadTemplateFromString) where++import Data.ByteString.Char8 (ByteString, pack)+import Debug.Trace+import System.FilePath+import Text.Parsec hiding (token)+import Text.Parsec.ByteString+import Text.Twine.Parser.Types+import Control.Monad++token t = do+ x <- string t+ spaces+ return t++template = templateEntities <|> textBlock++templateEntities = try slot <|> try conditional <|> try loop <|> try assign <|> include <?> "Template entity"++startOfEntities = try (string "{{") + <|> try (string "{@")+ <|> try (string "{|")+ <|> try (string "{+")+ <|> try (string "{?")+ <?> "start of entity"++endOfEntities = try (string "}}") + <|> try (string "@}")+ <|> try (string "|}")+ <|> try (string "+}")+ <|> try (string "?}")+ <?> "end of entity"++textBlock = do + text <- manyTill anyChar ((lookAhead startOfEntities >> return ()) <|> (lookAhead endOfEntities >> return ()) <|> eof)+ return (Text $ pack text)++slot = do+ token "{{" <?> "Start of slot"+ spaces+ expr <- expression+ spaces+ string "}}" <?> "End of slot"+ return (Slot expr)++loop = do+ token "{@"+ token "|" <?> "start of loop expression"+ ident <- name+ spaces+ token "<-"+ from <- expression+ spaces+ char '|' <?> "end of loop expression"+ blocks <- manyTill template (string "@}")+ return (Loop (from) ident blocks)++conditional = do+ token "{?"+ token "|" <?> "start of conditional expression"+ expr <- expression+ spaces+ char '|' <?> "end of conditional expression"+ blocks <- manyTill template (string "?}")+ return (Cond expr blocks)++assign = do+ token "{|"+ key <- name+ spaces+ token "="+ expr <- expression+ spaces+ string "|}"+ return (Assign key expr)++include = do+ token "{+"+ path <- try string' <|> many1 (noneOf " +") <?> "Filepath"+ spaces+ string "+}"+ return (Incl path)++------------------------------------------------------------------------+-- Expressions+------------------------------------------------------------------------++accessor = do+ a <- try method <|> try atom <?> "property or method"+ char '.'+ b <- expression+ return $ Accessor a b++method = do+ a <- name+ token "("+ expr <- sepBy expression (token ",")+ token ")"+ return $ Func a expr++sexpr = do+ token "("+ n <- name+ spaces+ expr <- sepBy expression' (space)+ token ")"+ return $ Func n expr++openExpr = do+ n <- name+ spaces+ expr <- sepBy1 expression' (space)+ return $ Func n expr++string' = do+ char '"'+ manyTill (noneOf "\"") (char '"')++stringLiteral = do+ st <- string'+ return (StringLiteral (pack st))++numberLiteral = do+ num <- many1 (digit)+ return (NumberLiteral (read num))++valid = (letter <|> (oneOf "#+-*$/?_") <|> digit)++name = do+ first <- try letter <|> oneOf "#+-*$/?_" + at <- many valid+ return (pack $ first : at)++atom = do+ n <- name+ return (Var n)++expression = try sexpr <|> try accessor <|> try method <|> try openExpr <|> try atom <|> try stringLiteral <|> numberLiteral <?> "expression"+expression' = try sexpr <|> try atom <|> try stringLiteral <|> numberLiteral <?> "expression"+------------------------------------------------------------------------++templateParser = manyTill template eof++parseTemplate name src = case parse templateParser name src of+ Right res -> res+ Left err -> error (show err)++parseFile fp = do + parsed <- parseFromFile templateParser fp + case parsed of+ Right res -> return res+ Left err -> error (show err)++doInclude base ps = foldM ax [] ps + where ax a (Incl fs) = do pf <- parseFile (base </> fs) + wi <- doInclude (takeDirectory (base </> fs)) pf+ return (a ++ wi)+ ax a x = return (a ++ [x])++------------------------------------------------------------------------+ +loadTemplateFromFile :: FilePath -> IO Template+loadTemplateFromFile fp = parseFile fp >>= doInclude (takeDirectory fp)++loadTemplateFromString :: String -> Template+loadTemplateFromString = parseTemplate "theTemplate"++++++
+ src/Text/Twine/Parser/Types.hs view
@@ -0,0 +1,30 @@+module Text.Twine.Parser.Types where++import qualified Data.ByteString.Char8 as C++type Key = C.ByteString+type Name = C.ByteString ++type Template = [TemplateCode]++data Expr = Func Name [Expr] + | Var Name + | StringLiteral C.ByteString+ | NumberLiteral Integer+ | Accessor Expr Expr+ deriving (Show,Read,Eq)++data TemplateCode = Text C.ByteString + | Slot Expr+ | Loop Expr Key [TemplateCode]+ | Cond Expr [TemplateCode]+ | Incl FilePath+ | Assign Key Expr+ deriving (Show)+++data ParserState = ParserState { getBlocks :: [TemplateCode] + , getTextQ :: C.ByteString+ , getTemplate :: C.ByteString } deriving (Show)++makePS = ParserState [] C.empty
+ twine.cabal view
@@ -0,0 +1,35 @@+name: twine+version: 0.1.1+synopsis: very simple template language+description: very simple template language+category: Text+license: BSD3+license-file: LICENSE+author: James Sanders+maintainer: jimmyjazz14@gmail.com+homepage: http://twine.james-sanders.com+build-depends: base+build-type: Simple+Cabal-Version: >= 1.2++Library+ hs-source-dirs: src+ Build-Depends: base >= 4 && < 5+ , mtl+ , bytestring >= 0.9.1.5+ , filepath >= 1.1.0.3+ , parsec >= 3+ , containers >= 0.3 ++ Exposed-modules: Text.Twine++ Other-modules: Text.Twine.Parser+ ,Text.Twine.Parser.Types+ ,Text.Twine.Interpreter+ ,Text.Twine.Interpreter.Context+ ,Text.Twine.Interpreter.ContextWriter + ,Text.Twine.Interpreter.FancyContext+ ,Text.Twine.Interpreter.Types+ ,Text.Twine.Interpreter.Builtins++ ghc-options: -O2 -fvia-C