packages feed

madlang 2.4.0.1 → 2.4.0.2

raw patch · 14 files changed

+179/−185 lines, 14 filesdep ~basedep ~megaparsecPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: base, megaparsec

API changes (from Hackage documentation)

- Text.Madlibs: makeTree :: [Text] -> FilePath -> FilePath -> IO (Either (ParseError Char Dec) RandTok)
+ Text.Madlibs: makeTree :: [Text] -> FilePath -> FilePath -> IO (Either (ParseError Char (ErrorFancy Void)) RandTok)
- Text.Madlibs: parseFile :: [Text] -> FilePath -> FilePath -> IO (Either (ParseError Char Dec) RandTok)
+ Text.Madlibs: parseFile :: [Text] -> FilePath -> FilePath -> IO (Either (ParseError Char (ErrorFancy Void)) RandTok)
- Text.Madlibs: parseTok :: FilePath -> [(Key, RandTok)] -> [Text] -> Text -> Either (ParseError Char Dec) RandTok
+ Text.Madlibs: parseTok :: FilePath -> [(Key, RandTok)] -> [Text] -> Text -> Either (ParseError Char (ErrorFancy Void)) RandTok

Files

README.md view
@@ -14,25 +14,30 @@  ## Examples -An example is worth a thousand words, so suppose you wanted to generate a mediocre fortune telling bot. You could write the following code:- ```madlang-:define person-    0.7 "A close friend will "-    0.3 "You will "-:define goodfortune-    0.2 person "make rain on the planet Mars"-    0.8 "nice things will happen today :)"-:define fortune-    0.5 "drink a boatload of milk"-    0.5 "get angry for no reason"-:define intense-    1.0 person "wrestle in the WWE".to_upper-    1.0 person "bite in a bottle of hot sauce".to_upper+# Madlang is a declarative language. The most basic component is a function, viz.+:define coinFlip+    1.0 "heads"+    1.0 "tails"++:define die+    1.0 "1"+    1.0 "2"+    1.0 "3"+    1.0 "4"+    1.0 "5"+    1.0 "6"++# Madlang also has categories, that is, a collection of functions that can be+# bundled together+:category gambling+    coinFlip+    die++# :return declarations handle the actual output :return-    0.7 person fortune-    0.1 intense-    0.2 goodfortune+    0.7 gambling+    0.3 gambling.to_upper # .to_upper is a modifier which make the whole string uppercase ```  ### Syntax
bash/mkCompletions view
@@ -1,4 +1,9 @@ #!/bin/bash-madlang --bash-completion-script `which madlang` > tmp-sudo cp tmp /etc/bash_completion.d/madlang-rm tmp+mkdir -p ~/.bash_completion.d+for BINARY in madlang+do+    $BINARY --bash-completion-script "$(which $BINARY)" > tmp+    cp tmp ~/.bash_completion.d/$BINARY+    rm tmp+done+printf "\n##Added by madlang\nfor c in ~/.bash_completion.d/* ; do\n    . \$c\ndone\n" >> ~/.bashrc
madlang.cabal view
@@ -1,5 +1,5 @@ name:                madlang-version:             2.4.0.1+version:             2.4.0.2 synopsis:            Randomized templating language DSL description:         Please see README.md homepage:            https://github.com/vmchale/madlang#readme@@ -51,7 +51,7 @@                      , Text.Madlibs.Exec.Main                      , Paths_madlang   build-depends:       base >= 4.7 && < 5-                     , megaparsec+                     , megaparsec >= 6.0                      , text                      , optparse-applicative                      , template-haskell
src/Text/Madlibs/Ana/Parse.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE OverloadedStrings #-}+ -- | Parse our DSL module Text.Madlibs.Ana.Parse (     parseTok@@ -11,14 +13,17 @@ import           Data.Composition import qualified Data.Map                    as M import           Data.Maybe+import           Data.Monoid import qualified Data.Text                   as T+import           Data.Void import           Text.Madlibs.Ana.ParseUtils import           Text.Madlibs.Cata.SemErr import           Text.Madlibs.Internal.Types import           Text.Madlibs.Internal.Utils import           Text.Megaparsec-import qualified Text.Megaparsec.Lexer       as L-import           Text.Megaparsec.Text+import           Text.Megaparsec.Char+import qualified Text.Megaparsec.Char.Lexer  as L+import           Text.Megaparsec.Pos  -- | Parse a lexeme, aka deal with whitespace nicely. lexeme :: Parser a -> Parser a@@ -29,7 +34,7 @@ spaceConsumer = L.space (void . some $ spaceChar) (L.skipLineComment "#") (L.skipBlockComment "{#" "#}")  -- | parse a symbol, i.e. string plus surrouding whitespace-symbol :: String -> Parser String+symbol :: T.Text -> Parser T.Text symbol = L.symbol spaceConsumer  -- | Parse a number/probability@@ -38,20 +43,20 @@  -- | Parse an integer integer :: Parser Integer-integer = lexeme (L.integer {--<|> parseNumber--}) <?> "Integer"+integer = lexeme L.decimal <?> "Integer"  -- | Make sure definition blocks start un-indented nonIndented = L.nonIndented spaceConsumer  -- | Make contents of definition blocks are indented.-indentGuard = L.indentGuard spaceConsumer GT (unsafePos 4)+indentGuard = L.indentGuard spaceConsumer GT (mkPos 4)  -- | Parse between quotes quote :: Parser a -> Parser a quote = between .$ (char '"')  -- | Parse a keyword-keyword :: String -> Parser String+keyword :: T.Text -> Parser T.Text keyword str = (char ':') >> (symbol str) <?> "keyword"  -- | Parse a var@@ -81,15 +86,15 @@     <?> "return block"  -- | Parse a template name (what follows a `:define` or `return` block)-name :: Parser String-name = lexeme (some (letterChar <|> oneOf ("-/" :: String))) <?> "template name" -- TODO make this broader in terms of what it includes+name :: Parser T.Text+name = (lexeme . fmap T.pack) (some (letterChar <|> oneOf ("-/" :: String))) <?> "template name" -- TODO make this broader in terms of what it includes  -- | Parse a modifier modifier :: Parser (T.Text -> T.Text) modifier = do     char '.'     str <- foldr (<|>) (pure "") $ map (try . string) ["to_upper", "to_lower", "reverse", "reverse_words", "oulipo", "capitalize"]-    pure (fromMaybe id (M.lookup str modifierList)) <?> "modifier"+    pure (fromMaybe id (M.lookup (T.unpack str) modifierList)) <?> "modifier"  -- | Parse template into a `PreTok` of referents and strings preStr :: [T.Text] -> Parser PreTok@@ -97,7 +102,7 @@         n <- name ;         mod <- many modifier ;         spaceConsumer ;-        pure $ Name (T.pack n) (foldr (.) id mod)+        pure $ Name n (foldr (.) id mod)     } <|>     do {         v <- var ;@@ -129,12 +134,12 @@     pure (1.0, [str]) <?> "Function name"  -- | Parse an `include`-inclusions :: Parser [String]+inclusions :: Parser [T.Text] inclusions = many . try $ do     include     str <- name     string ".mad"-    pure (str ++ ".mad")+    pure (str <> ".mad")  -- | Parse a `define` block definition :: [T.Text] -> Parser (Key, [(Prob, [PreTok])])@@ -142,7 +147,7 @@     define     str <- name     val <- fmap normalize . some $ pair ins-    pure (T.pack str, val) <?> "define block"+    pure (str, val) <?> "define block"  -- | Parse a `category` block category :: Parser (Key, [(Prob, [PreTok])])@@ -150,7 +155,7 @@     cat     str <- name     val <- fmap normalize . some $ function-    pure (T.pack str, val) <?> "category block"+    pure (str, val) <?> "category block"  -- | Parse the `:return` block final :: [T.Text] -> Parser [(Prob, [PreTok])]@@ -176,12 +181,12 @@ parseTreeM ins = buildTree <$> program ins  -- | Parse text as a list of functions-parseTokF :: FilePath -> [(Key, RandTok)] -> [T.Text] -> T.Text -> Either (ParseError Char Dec) [(Key, RandTok)]+parseTokF :: FilePath -> [(Key, RandTok)] -> [T.Text] -> T.Text -> Either (ParseError Char (ErrorFancy Void)) [(Key, RandTok)] parseTokF filename state ins f = (flip execState (filterTemplate state)) <$> runParser (parseTokM ins) filename f     where filterTemplate = map (\(i,j) -> if i == "Return" then (strip filename, j) else (i,j)) -- TODO fix the extras  -- | Parse text as a list of tokens, suitable for printing as a tree.-parseTreeF :: FilePath -> [(Key, RandTok)] -> [T.Text] -> T.Text -> Either (ParseError Char Dec) [(Key, RandTok)]+parseTreeF :: FilePath -> [(Key, RandTok)] -> [T.Text] -> T.Text -> Either (ParseError Char (ErrorFancy Void)) [(Key, RandTok)] parseTreeF filename state ins f = (flip execState (filterTemplate state)) <$> runParser (parseTreeM ins) filename f     where filterTemplate = map (\(i,j) -> if i == "Return" then (strip filename, j) else (i,j)) @@ -196,13 +201,13 @@     -> [(Key, RandTok)] -- ^ Context, i.e. other random data paired with a key.     -> [T.Text] -- ^ list of variables to substitute into the template     -> T.Text -- ^ Actaul text to parse-    -> Either (ParseError Char Dec) RandTok -- ^ Result+    -> Either (ParseError Char (ErrorFancy Void)) RandTok -- ^ Result parseTok = (fmap takeTemplate) .*** parseTokF  -- | Parse text as a token, suitable for printing as a tree..-parseTree :: FilePath -> [(Key, RandTok)] -> [T.Text] -> T.Text -> Either (ParseError Char Dec) RandTok+parseTree :: FilePath -> [(Key, RandTok)] -> [T.Text] -> T.Text -> Either (ParseError Char (ErrorFancy Void)) RandTok parseTree = (fmap takeTemplate) .*** parseTreeF  -- | Parse inclustions-parseInclusions :: FilePath -> T.Text -> Either (ParseError Char Dec) [String]+parseInclusions :: FilePath -> T.Text -> Either (ParseError Char (ErrorFancy Void)) [T.Text] parseInclusions = runParser inclusions
src/Text/Madlibs/Ana/ParseUtils.hs view
@@ -8,27 +8,25 @@   , buildTree   ) where -import Text.Madlibs.Internal.Types-import Text.Madlibs.Internal.Utils-import Text.Madlibs.Cata.SemErr-import Data.List-import qualified Data.Text as T-import Control.Monad.State-import Data.Foldable-import Control.Arrow-import System.Random.Shuffle-import qualified Data.Map as M-import Data.Char-import Data.Monoid-import Data.Function.Contravariant.Syntax-import Control.Monad.Random.Class+import           Control.Arrow+import           Control.Monad.Random.Class+import           Control.Monad.State+import           Data.Char+import           Data.Foldable+import           Data.List+import qualified Data.Map                    as M+import qualified Data.Text                   as T+import           System.Random.Shuffle+import           Text.Madlibs.Cata.SemErr+import           Text.Madlibs.Internal.Types+import           Text.Madlibs.Internal.Utils  --TODO consider moving Ana.ParseUtils to Cata.Sorting  (&) :: a -> (a -> b) -> b (&) x f = f x --- | A map with all the modifiers we +-- | A map with all the modifiers for Madlang modifierList :: M.Map String (T.Text -> T.Text) modifierList = M.fromList [("to_upper", T.map toUpper)     , ("to_lower", T.map toLower)@@ -47,28 +45,28 @@  -- | Get the :return value takeTemplate :: [(Key, RandTok)] -> RandTok-takeTemplate = snd . head . filter (\(i,j) -> i == "Return")+takeTemplate = snd . headNoReturn . filter (\(i,j) -> i == "Return")  -- | Convert the stuff after the number to a `RandTok` concatTok :: T.Text -> Context [PreTok] -> Context RandTok concatTok param pretoks = do     ctx <- get     let unList (List a) = a-    let toRand (Name str f) = (apply f) . List . snd . (head' str param) . (filter ((== str) . fst)) . (map (second unList)) $ ctx-- TODO fix head' which can fail because of lack of scope too+    let toRand (Name str f) = (apply f) . List . snd . (head' str param) . (filter ((== str) . fst)) . (map (second unList)) $ ctx         toRand (PreTok txt) = Value txt     fold . (map toRand) <$> pretoks --- | Build token in tree structure, without concatenating. +-- | Build token in tree structure, without concatenating. buildTok :: T.Text -> Context [PreTok] -> Context RandTok buildTok param pretoks = do     ctx <- get     let unList (List a) = a-    let toRand (Name str f) = (apply f) . List . snd . (head' str param) . (filter ((==str) . fst)) . (map (second unList)) $ ctx +    let toRand (Name str f) = (apply f) . List . snd . (head' str param) . (filter ((== str) . fst)) . (map (second unList)) $ ctx         toRand (PreTok txt) = Value txt     List . zip ([1..]) . (map toRand) <$> pretoks  -- | Build the token without concatenating, yielding a `RandTok` suitable to be--- printed as a tree. +-- printed as a tree. buildTree :: [(Key, [(Prob, [PreTok])])] -> Context RandTok buildTree list@[(key,pairs)] = do     toks <- mapM (\(i,j) -> buildTok key (pure j)) pairs@@ -96,17 +94,19 @@ sortKeys :: [(Key, [(Prob, [PreTok])])] -> [(Key, [(Prob, [PreTok])])] sortKeys = sortBy orderKeys +orderHelper :: Key -> [(Prob, [PreTok])] -> Bool+orderHelper key = any (\pair -> key `elem` (map unTok . snd $ pair))+ -- | Ordering on the keys to account for dependency orderKeys :: (Key, [(Prob, [PreTok])]) -> (Key, [(Prob, [PreTok])]) -> Ordering orderKeys (key1, l1) (key2, l2)     | key1 == "Return" = GT     | key2 == "Return" = LT-    | any (\pair -> {--hyphenList key1 (map unTok . snd $ pair) ||--} key1 `elem` (map unTok . snd $ pair)) l2 = LT-    | any (\pair -> {--hyphenList key2 (map unTok . snd $ pair) ||--} key2 `elem` (map unTok . snd $ pair)) l1 = GT+    | orderHelper key1 l2 = LT+    | orderHelper key2 l1 = GT+    | any (flip orderHelper l1) (flatten l2) = LT -- FIXME needs to not default to `EQ` when transitive dependencies are a thing.+    | any (flip orderHelper l2) (flatten l1) = GT     | otherwise = EQ -hyphenList :: T.Text -> [T.Text] -> Bool-hyphenList s1 = any (flip hyphenSuffix s1)--hyphenSuffix :: T.Text -> T.Text -> Bool-hyphenSuffix = ("-" <>) -.* T.isSuffixOf+flatten :: [(Prob, [PreTok])] -> [Key]+flatten = concatMap (map unTok . snd)
src/Text/Madlibs/Ana/Resolve.hs view
@@ -7,45 +7,46 @@   , makeTree   , runText ) where -import qualified Data.Text as T-import Text.Megaparsec hiding (try)-import Text.Madlibs.Internal.Types-import Text.Madlibs.Internal.Utils-import Text.Madlibs.Ana.Parse-import Text.Madlibs.Ana.ParseUtils-import Text.Madlibs.Cata.Run-import Data.Composition-import System.Directory-import Lens.Micro-import Data.Monoid-import Control.Monad.Random.Class-import System.Environment-import Control.Exception+import           Control.Exception+import           Control.Monad.Random.Class+import           Data.Composition+import           Data.Monoid+import qualified Data.Text                   as T+import           Data.Void+import           Lens.Micro+import           System.Directory+import           System.Environment+import           Text.Madlibs.Ana.Parse+import           Text.Madlibs.Ana.ParseUtils+import           Text.Madlibs.Cata.Run+import           Text.Madlibs.Internal.Types+import           Text.Madlibs.Internal.Utils+import           Text.Megaparsec             hiding (parseErrorPretty', try)  -- | Parse a template file into the `RandTok` data type parseFile :: [T.Text] -- ^ variables to substitute into the template     -> FilePath -- ^ folder     -> FilePath -- ^ filepath within folder-    -> IO (Either (ParseError Char Dec) RandTok) -- ^ parsed `RandTok`+    -> IO (Either (ParseError Char (ErrorFancy Void)) RandTok) -- ^ parsed `RandTok` parseFile = fmap (fmap takeTemplate) .** (getInclusionCtx False)  -- | Generate text from file with inclusions-getInclusionCtx :: Bool -> [T.Text] -> FilePath -> FilePath -> IO (Either (ParseError Char Dec) [(Key, RandTok)])+getInclusionCtx :: Bool -> [T.Text] -> FilePath -> FilePath -> IO (Either (ParseError Char (ErrorFancy Void)) [(Key, RandTok)]) getInclusionCtx isTree ins folder filepath = do     file <- catch (readFile' (folder ++ filepath)) (const (do { home <- getEnv "HOME" ; readFile' (home <> "/.madlang/" <> folder <> filepath) } ) :: IOException -> IO T.Text)-    let filenames = either (error . show) id $ parseInclusions filepath file -- TODO pass up errors correctly+    let filenames = map T.unpack $ either (error . show) id $ parseInclusions filepath file -- TODO pass up errors correctly     let resolveKeys file = map (over _1 ((((T.pack . (<> "-")) . dropExtension) file) <>))     ctxPure <- mapM (getInclusionCtx isTree ins folder) filenames     let ctx = (zipWith resolveKeys filenames) <$> sequence ctxPure     catch         (parseCtx isTree ins (concat . (either (const []) id) $ ctx) (folder ++ filepath))-        ((const (do { home <- getEnv "HOME" ; parseCtx isTree ins (concat . (either (const []) id) $ ctx) (home <> "/.madlang/" <> folder <> filepath) }) ) :: IOException -> IO (Either (ParseError Char Dec) [(Key, RandTok)]))+        ((const (do { home <- getEnv "HOME" ; parseCtx isTree ins (concat . (either (const []) id) $ ctx) (home <> "/.madlang/" <> folder <> filepath) }) ) :: IOException -> IO (Either (ParseError Char (ErrorFancy Void)) [(Key, RandTok)]))  -- | Generate randomized text from a file containing a template runFile :: [T.Text] -- ^ List of variables to substitute into the template     -> FilePath -- ^ Path to @.mad@ file.     -> IO T.Text -- ^ Result-runFile ins toFolder = do +runFile ins toFolder = do     exists <- doesDirectoryExist (getDir toFolder)     let filepath = reverse . (takeWhile (/='/')) . reverse $ toFolder     runInFolder ins (getDir toFolder) filepath@@ -59,12 +60,12 @@ runText vars name = (either (pure . parseErrorPretty') id) . (fmap run) . (parseTok name [] vars)  -- | Get file as context-parseCtx :: Bool -> [T.Text] -> [(Key, RandTok)] -> FilePath -> IO (Either (ParseError Char Dec) [(Key, RandTok)])+parseCtx :: Bool -> [T.Text] -> [(Key, RandTok)] -> FilePath -> IO (Either (ParseError Char (ErrorFancy Void)) [(Key, RandTok)]) parseCtx isTree ins state filepath = do     txt <- readFile' filepath     let keys = (if isTree then parseTreeF else parseTokF) filepath state ins txt     pure keys  -- | Parse a template into a RandTok suitable to be displayed as a tree-makeTree :: [T.Text] -> FilePath -> FilePath -> IO (Either (ParseError Char Dec) RandTok)+makeTree :: [T.Text] -> FilePath -> FilePath -> IO (Either (ParseError Char (ErrorFancy Void)) RandTok) makeTree = fmap (fmap takeTemplate) .** (getInclusionCtx True)
src/Text/Madlibs/Cata/Run.hs view
@@ -1,10 +1,10 @@ -- | Module containing functions to get `Text` from `RandTok` module Text.Madlibs.Cata.Run (run) where -import Text.Madlibs.Internal.Types-import Text.Madlibs.Internal.Utils-import qualified Data.Text as T-import Control.Monad.Random.Class+import           Control.Monad.Random.Class+import qualified Data.Text                   as T+import           Text.Madlibs.Internal.Types+import           Text.Madlibs.Internal.Utils  -- | Generate randomized text from a `RandTok` --@@ -13,13 +13,13 @@ -- getText = do --     let exampleTok = List [(1.0,List [(0.5,Value "heads"),(0.5,Value "tails")])] --     run exampleTok--- @ +-- @ run :: (MonadRandom m) => RandTok -> m T.Text run tok@(List rs) = do     value <- getRandomR (0,1)     let ret = ((snd . head) . filter ((>=value) . fst)) $ mkCdf tok     case ret of-        (Value txt) -> pure txt+        (Value txt)   -> pure txt         tok@(List rs) -> run tok run (Value txt) = pure txt 
src/Text/Madlibs/Cata/SemErr.hs view
@@ -3,23 +3,28 @@ -- | Module defining the SemErr data type module Text.Madlibs.Cata.SemErr (     SemanticError (..)+  , Parser   , access   , checkSemantics-  , head' ) where+  , head'+  , headNoReturn ) where  -import Text.Madlibs.Internal.Types-import Data.Typeable-import Text.PrettyPrint.ANSI.Leijen-import Control.Exception-import qualified Data.Text as T-import Control.Monad-import qualified Data.Set as S-import Text.Megaparsec.Text-import Text.Megaparsec.Prim-import Text.Megaparsec.Error-import Text.Madlibs.Internal.Utils+import           Control.Exception+import           Control.Monad+import qualified Data.Set                     as S+import qualified Data.Text                    as T+import           Data.Typeable+import           Data.Void+import           Text.Madlibs.Internal.Types+import           Text.Madlibs.Internal.Utils+import           Text.Megaparsec+import           Text.Megaparsec.Char+import           Text.Megaparsec.Error+import           Text.PrettyPrint.ANSI.Leijen +type Parser = Parsec (ErrorFancy Void) T.Text+ -- | Datatype for a semantic error data SemanticError = NoReturn | CircularFunctionCalls T.Text T.Text | InsufficientArgs Int Int | DoubleDefinition T.Text | NoContext T.Text     deriving (Typeable)@@ -29,15 +34,15 @@     show (DoubleDefinition f) = show $ semErrStart <> text "File contains two declarations of:" <> indent 4 (yellow (text' f))     show NoReturn = show $ semErrStart <> text "File must contain exactly one declaration of :return"     show (NoContext f1) = show $ semErrStart <> text "Call in function: " <> indent 4 (yellow (text' f1)) <> "which is not in scope"-    show (CircularFunctionCalls f1 f2) = show $ semErrStart <> text "Circular function declaration between:" <> indent 4 (yellow $ (text' f1) <> (text ", ") <> (text' f2))-    show (InsufficientArgs i j) = show $ semErrStart <> text "Insufficent arguments from the command line, given " <> (text . show $ i) <> ", expected at least " <> (text . show $ j)+    show (CircularFunctionCalls f1 f2) = show $ semErrStart <> text "Function" </> indent 4 (yellow (text' f2)) <> text' " refers to a function" </> indent 4 (yellow (text' f1)) <> text' ", which is not in scope." </> indent 2 (text' "This may be due to circular function dependecies.")+    show (InsufficientArgs i j) = show $ semErrStart <> text "Insufficent arguments from the command line; given " <> (text . show $ i) <> ", expected at least " <> (text . show $ j)  -- | Derived via our show instance; instance Exception SemanticError where  -- | Throw custom error given by string, within the parser customError :: String -> Parser a-customError = (failure .$ S.empty) . S.singleton . representFail+customError = fail  showCustomError :: (Show a) => a -> Parser b showCustomError = customError . show@@ -75,7 +80,7 @@ checkSemantics keys = foldr (<=<) pure ((checkKey "Return"):[checkKey key | key <- allKeys keys ]) keys     where allKeys = fmap name . (concatMap snd) . (concatMap snd)--traversal?           name (Name str _) = str-          name (PreTok _) = "Return"+          name (PreTok _)   = "Return"  -- | helper to filter out stuff that doesn't sumProb :: [(Prob, [PreTok])] -> Bool@@ -85,9 +90,13 @@ -- | Take the head of the list, or throw the appropriate error given which functions we are trying to call. head' :: T.Text -> T.Text -> [a] -> a head' _ _ (x:xs) = x-head' f1 f2 _ = throw (CircularFunctionCalls f1 f2)+head' f1 f2 _    = throw (CircularFunctionCalls f1 f2) --- | Access argument, or throw error if the list is too short. +headNoReturn :: [a] -> a+headNoReturn (x:xs) = x+headNoReturn _      = throw NoReturn++-- | Access argument, or throw error if the list is too short. access :: [a] -> Int -> a access xs i = if (i >= length xs) then throw (InsufficientArgs (length xs) (i+1)) else xs !! i 
src/Text/Madlibs/Generate/TH.hs view
@@ -1,22 +1,23 @@ {-# LANGUAGE TemplateHaskell #-}  -- | Module containing Quasi-Quoter and Template Haskell splice for use as an EDSL.-module Text.Madlibs.Generate.TH +module Text.Madlibs.Generate.TH     ( madFile     , madlang     ) where -import Language.Haskell.TH hiding (Dec)-import qualified Data.Text as T-import Text.Madlibs.Ana.Parse-import Text.Madlibs.Internal.Utils-import Language.Haskell.TH.Quote-import Data.FileEmbed-import Text.Megaparsec-import Data.Text.Encoding+import           Data.FileEmbed+import qualified Data.Text                   as T+import           Data.Text.Encoding+import           Data.Void+import           Language.Haskell.TH         hiding (Dec)+import           Language.Haskell.TH.Quote+import           Text.Madlibs.Ana.Parse+import           Text.Madlibs.Internal.Utils+import           Text.Megaparsec  -- | `QuasiQuoter` for an EDSL, e.g.--- +-- -- @ -- demoQQ :: T.Text -- demoQQ = run@@ -44,7 +45,7 @@     pure $ (VarE 'errorgen) `AppE` (parse `AppE` ((VarE 'T.pack) `AppE` (LitE (StringL (txt)))))  -- | Turn a parse error into an error that will be caught when Template Haskell compiles at runtime.-errorgen :: Either (ParseError Char Dec) a -> a+errorgen :: Either (ParseError Char (ErrorFancy Void)) a -> a errorgen = either (error . T.unpack . show') id  -- | Splice for embedding a '.mad' file, e.g.
src/Text/Madlibs/Internal/Types.hs view
@@ -1,13 +1,13 @@+{-# LANGUAGE FlexibleInstances    #-} {-# LANGUAGE TypeSynonymInstances #-}-{-# LANGUAGE FlexibleInstances #-}  -- | Module with the type of a random token module Text.Madlibs.Internal.Types where -import qualified Data.Text as T-import Control.Monad.State-import Lens.Micro-import Data.Function+import           Control.Monad.State+import           Data.Function+import qualified Data.Text           as T+import           Lens.Micro  -- | datatype for a double representing a probability type Prob = Double@@ -18,6 +18,10 @@ -- | Pretoken, aka token as first read in data PreTok = Name T.Text (T.Text -> T.Text) | PreTok T.Text +instance Show PreTok where+    show (Name t _) = show t+    show (PreTok t) = show t+ instance Eq PreTok where     (==) (Name a _) (PreTok b) = False     (==) (Name a1 f1) (Name a2 f2) = a1 == a2 && (f1 . T.pack $ ['a'..'z']) == (f2 . T.pack $ ['a'..'z'])@@ -29,16 +33,16 @@  apply :: (T.Text -> T.Text) -> RandTok -> RandTok -- TODO make a base functor so we can map f over stuff? apply f (Value str) = Value (f str)-apply f (List l) = List $ map (over _2 (apply f)) l+apply f (List l)    = List $ map (over _2 (apply f)) l --- | Make `RandTok` a monoid so we can append them together nicely (since they do generate text). +-- | Make `RandTok` a monoid so we can append them together nicely (since they do generate text). -- -- > (Value "Hello") <> (List [(0.5," you"), (0.5, " me")]) -- > (List [(0.5,"Hello you"), (0.5, "Hello me")]) instance Monoid RandTok where     mempty = Value ""     mappend (Value v1) (Value v2) = Value (T.append v1 v2)-    mappend (List l1) v@(Value v1) = List $ map (over (_2) (`mappend` v)) l1 +    mappend (List l1) v@(Value v1) = List $ map (over (_2) (`mappend` v)) l1     mappend v@(Value v2) (List l2) = List $ map (over (_2) (v `mappend`)) l2     mappend l@(List l1) (List l2) = List [ (p, l `mappend` tok) | (p,tok) <- l2 ] 
src/Text/Madlibs/Internal/Utils.hs view
@@ -1,13 +1,14 @@+{-# LANGUAGE FlexibleInstances    #-} {-# LANGUAGE TypeSynonymInstances #-}-{-# LANGUAGE FlexibleInstances #-}  -- | Internal utils to help out elsewhere module Text.Madlibs.Internal.Utils where -import Text.Madlibs.Internal.Types-import Text.Megaparsec.Error-import qualified Data.Text as T-import Lens.Micro+import qualified Data.Text                   as T+import           Data.Void+import           Lens.Micro+import           Text.Madlibs.Internal.Types+import           Text.Megaparsec.Error  -- | Drop file Extension dropExtension :: FilePath -> FilePath@@ -43,7 +44,7 @@ show' = (T.drop 1) . T.init . T.pack . show  -- | Pretty-print a ParseError-parseErrorPretty' :: ParseError Char Dec -> T.Text+parseErrorPretty' :: ParseError Char (ErrorFancy Void) -> T.Text parseErrorPretty' = T.pack . parseErrorPretty  -- | Strip a pre-token's name
stack.yaml view
@@ -1,45 +1,6 @@-resolver: lts-8.19+resolver: lts-9.0 packages:- - .- - extra-dep: true-   location:-       git: https://github.com/haskell/deepseq-       commit: 0b22c9825ef79c1ee41d2f19e7c997f5cdc93494- - extra-dep: true-   location:-       git: https://github.com/ekmett/semigroupoids.git-       commit: c3297f970658ae874db098190327ec742044a2e6- - extra-dep: true-   location:-       git: http://github.com/ekmett/lens.git-       commit: 7031e00f62a704a86c3149c75c1e2afc059af022-   #- extra-dep: true-   #location:-   #    git: https://github.com/ekmett/trifecta.git-   #    commit: ea93f257e18e14699f8872a88bfd444c73df9155- - extra-dep: true-   location:-       git: https://github.com/dreixel/syb.git-       commit: f584ecd525179778063df2eb02dc6bbe406d7e75-flags:-    madlang:-        llvm-fast: true-#extra-deps:-#- dhall-1.4.2-allow-newer: true-compiler: ghc-8.2.0.20170507-compiler-check: match-exact-setup-info:- ghc:-  linux64:-   8.2.0.20170507:-    url: https://downloads.haskell.org/~ghc/8.2.1-rc2/ghc-8.2.0.20170507-x86_64-deb8-linux.tar.xz-  linux64-nopie:-   8.2.0.20170507:-    url: https://downloads.haskell.org/~ghc/8.2.1-rc2/ghc-8.2.0.20170507-x86_64-deb8-linux.tar.xz-  macosx:-   8.2.0.20170507:-    url: https://downloads.haskell.org/~ghc/8.2.1-rc2/ghc-8.2.0.20170507-x86_64-apple-darwin.tar.xz-  windows64:-   8.2.0.20170507:-    url: https://downloads.haskell.org/~ghc/8.2.1-rc2/ghc-8.2.0.20170507-x86_64-unknown-mingw32.tar.xz+- .+extra-deps: []+flags: {}+extra-package-dbs: []
test/Spec.hs view
@@ -36,9 +36,6 @@         parallel $ it "fails when variables are not passed in" $ do             file <- madVar             (parseTok "" [] [] `shouldFailOn` file) `shouldThrow` anyException-        --parallel $ it "parses tibetan numerals" $ do-        --    file <- madFileTibetan-        --    parseTok "" [] file `shouldParse` (List [(1.0,List [(0.5,Value "heads"),(0.5,Value "tails")])])     describe "runFile" $ do         parallel $ it "parses nested modifiers and modifiers on variables correctly" $ \file -> do             runFile ["aa"] "test/templates/modifiers.mad" >>= (`shouldSatisfy` (\a -> any (a==) ["AAAaaa","AAAaa","AAaaa","AAaa"]))
test/templates/ordered.mad view
@@ -3,6 +3,10 @@     1.0 "two"     1.0 "three" +:define afourththing+    1.0 something+    1.0 "fourth".oulipo+ :define something     1.0 "heads"     1.0 "tails"@@ -14,3 +18,4 @@  :return     1.0 something+    1.0 afourththing