packages feed

ascii-art-to-unicode (empty) → 0.1.0.0

raw patch · 8 files changed

+812/−0 lines, 8 filesdep +ascii-art-to-unicodedep +basedep +comonadsetup-changed

Dependencies added: ascii-art-to-unicode, base, comonad, doctest, strict

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Franz Thoma (c) 2017++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.++    * Neither the name of Franz Thoma nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++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+OWNER 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.
+ README.md view
@@ -0,0 +1,68 @@+ASCII art to Unicode converter+==============================++Small CLI program to convert ASCII box drawings to unicode. Inspired+by [svgbob](https://github.com/ivanceras/svgbobrus)+and+[The Monads Hidden Behind Every Zipper](http://blog.sigfpe.com/2007/01/monads-hidden-behind-every-zipper.html).+++```+> aa2u++-------------+             |+| Hello World |             |  -----+++-------------+             |       |     +--++                            |       +-----+  |+----------------------------+                |+                            |         +------++          +-----+---.       |         |+          |     +---+       |   +--+  |+      .---|         |---.   |   |  |  |+      |   +---------+   |#  |   |  |  |+      |                 |#  |   |  +--++      '-----------------'#  |   +---------++        ##################  |+++------+-----+---------++| This | is  | a table |++------+-----+---------++| It   | has | some    |++------+-----+---------++| values     |         |++------+-----+---------++| 0    | 1   | 2  | 3  |++------+-----+----+----++^D+┌─────────────┐             │+│ Hello World │             │  ─────┐+└─────────────┘             │       │     ┌──┐+                            │       └─────┘  │+────────────────────────────┤                │+                            │         ┌──────┘+          ┌─────┬───╮       │         │+          │     └───┤       │   ┌──┐  │+      ╭───│         │───╮   │   │  │  │+      │   └─────────┘   │█  │   │  │  │+      │                 │█  │   │  └──┘+      ╰─────────────────╯█  │   └──────────+        ██████████████████  │++┌──────┬─────┬─────────┐+│ This │ is  │ a table │+├──────┼─────┼─────────┤+│ It   │ has │ some    │+├──────┴─────┼─────────┤+│ values     │         │+├──────┬─────┼─────────┤+│ 0    │ 1   │ 2  │ 3  │+└──────┴─────┴────┴────┘+```++### Usage:++```bash+aa2u > outfile  # Reads from stdin+aa2u < infile   # Prints to stdout+aa2u infile     # Input file as argument works as well+aa2u < infile > outfile  # Convert a file+```
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/Main.hs view
@@ -0,0 +1,46 @@+{-# LANGUAGE LambdaCase #-}+module Main where++import           System.Environment+import qualified System.IO.Strict as Strict+import           Text.AsciiArt++data Mode+    = Inplace FilePath+    | Read FilePath+    | Pipe++main :: IO ()+main = do+    mode <- getArgs <&> \case+        []           -> Pipe+        ["-i", file] -> Inplace file+        [file]       -> Read file+        _            -> error "Usage: \n\+                              \    aa2u            # reads from stdin, prints to stdout\n\+                              \    aa2u FILE       # reads FILE, prints to stdout\n\+                              \    aa2u -i FILE    # reads from and writes to FILE\n"+    input <- case mode of+        Inplace file -> Strict.readFile file+        Read    file -> readFile file+        Pipe         -> getContents+    let inputLines = lines input+        plane = planeFromList ' ' inputLines+        width = maximum (fmap length inputLines)+        height = length inputLines+    let unicodeArt+            = unlines+            . fmap trimRight+            . planeToList height width+            . renderAsciiToUnicode+            $ plane+    case mode of+        Inplace file -> writeFile file unicodeArt+        Read    _    -> putStr unicodeArt+        Pipe         -> putStr unicodeArt++trimRight :: String -> String+trimRight = reverse . dropWhile (== ' ') . reverse++(<&>) :: Functor f => f a -> (a -> b) -> f b+(<&>) = flip (<$>)
+ ascii-art-to-unicode.cabal view
@@ -0,0 +1,56 @@+name:                ascii-art-to-unicode+version:             0.1.0.0+synopsis:            ASCII Art to Unicode Box Drawing converter+description:+  @aa2u@ converts ASCII Art box drawings to Unicode.+  .+  > > aa2u+  > +-------------++  > | Hello World |+  > +-------------++  > ^D+  > ┌─────────────┐+  > │ Hello World │+  > └─────────────┘+  .+  For more examples see the @doctest@ suite in "Text.AsciiArt".+homepage:            https://github.com/fmthoma/ascii-art-to-unicode#readme+license:             BSD3+license-file:        LICENSE+author:              Franz Thoma+maintainer:          f.m.thoma@googlemail.com+copyright:           2017 Franz Thoma+build-type:          Simple+extra-source-files:  README.md+                   , stack.yaml+cabal-version:       >=1.10++library+  hs-source-dirs:      src+  exposed-modules:     Text.AsciiArt+  build-depends:       base >= 4.7 && < 5+                     , comonad+  default-language:    Haskell2010++executable aa2u+  hs-source-dirs:      app+  main-is:             Main.hs+  ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N+  build-depends:       base+                     , ascii-art-to-unicode+                     , strict+  default-language:    Haskell2010++test-suite ascii-art-to-unicode-test+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Spec.hs+  build-depends:       base+                     , ascii-art-to-unicode+                     , doctest+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N+  default-language:    Haskell2010++source-repository head+  type:     git+  location: https://github.com/githubuser/ascii-art-to-unicode
+ src/Text/AsciiArt.hs view
@@ -0,0 +1,532 @@+{- |+=Basic Elements++==Simple Boxes++>>> :{+aa2u "++  +-----+  +--+--+-----+  \n\+     \++  +--+  |  |  |  |     |  \n\+     \       |  |  +--+--+--+--+  \n\+     \+---+  |  |  |     |  |  |  \n\+     \+---+  +--+  +-----+--+--+  "+:}+┌┐  ┌─────┐  ┌──┬──┬─────┐+└┘  └──┐  │  │  │  │     │+       │  │  ├──┴──┼──┬──┤+┌───┐  │  │  │     │  │  │+└───┘  └──┘  └─────┴──┴──┘++==Rounded Boxes++>>> :{+aa2u "..  .-----.  .--+--+-----.  \n\+     \''  '--.  |  |  |  |     |  \n\+     \       |  |  +--+--+--+--+  \n\+     \.---.  |  |  |     |  |  |  \n\+     \'---'  '--'  '-----+--+--'  "+:}+╭╮  ╭─────╮  ╭──┬──┬─────╮+╰╯  ╰──╮  │  │  │  │     │+       │  │  ├──┴──┼──┬──┤+╭───╮  │  │  │     │  │  │+╰───╯  ╰──╯  ╰─────┴──┴──╯++==Dotted and double strokes++>>> :{+aa2u "++  .-----.  +==+==+=====+  \n\+     \++  +==+  :  |  :  |     |  \n\+     \       :  :  +==+==+==+==+  \n\+     \+===+  :  :  |     |  :  |  \n\+     \+---+  '--'  +=====+==+==+  "+:}+┌┐  ╭─────╮  ╒══╤══╤═════╕+└┘  ╘══╕  ┆  │  ┆  │     │+       ┆  ┆  ╞══╧══╪══╤══╡+╒═══╕  ┆  ┆  │     │  ┆  │+└───┘  ╰──╯  ╘═════╧══╧══╛++==Cast shadows++>>> :{+aa2u "+-------------+   \n\+     \|             |   \n\+     \+---+     +---+#  \n\+     \  ##|     |#####  \n\+     \    |     |#      \n\+     \    +-----+#      \n\+     \      ######      "+:}+┌─────────────┐+│             │+└───┐     ┌───┘█+  ██│     │█████+    │     │█+    └─────┘█+      ██████++=Properties++==Idempotent++Already rendered portions are not affected:++>>> :{+aa2u "┌┐  ╭─────╮  ╒══╤══╤═════╕  ┌───┐   \n\+     \└┘  ╘══╕  ┆  │  ┆  │     │  │   │   \n\+     \       ┆  ┆  ╞══╧══╪══╤══╡  │   │█  \n\+     \╒═══╕  ┆  ┆  │     │  ┆  │  └───┘█  \n\+     \└───┘  ╰──╯  ╘═════╧══╧══╛    ████  "+:}+┌┐  ╭─────╮  ╒══╤══╤═════╕  ┌───┐+└┘  ╘══╕  ┆  │  ┆  │     │  │   │+       ┆  ┆  ╞══╧══╪══╤══╡  │   │█+╒═══╕  ┆  ┆  │     │  ┆  │  └───┘█+└───┘  ╰──╯  ╘═════╧══╧══╛    ████++==Incremental++Existing characters can be removed:++>>> :{+aa2u "┌──┬ ┬──┐\n\+     \   │ │  │\n\+     \╞══╪ ╪══╡\n\+     \│        \n\+     \├──┼ ┼──┤\n\+     \   │ │  │\n\+     \└──┴ ┴──┘"+:}+───┐ ┌──┐+   │ │  │+╒══╛ ╘══╛+│+└──┐ ┌──┐+   │ │  │+───┘ └──┘++New connections can be added:++>>> :{+aa2u "┌──┐-┌──┐\n\+     \│  │ │  │\n\+     \╘══╛=╘══╛\n\+     \|  : :  |\n\+     \┌──┐-┌──┐\n\+     \│  │ │  │\n\+     \└──┘-└──┘"+:}+┌──┬─┬──┐+│  │ │  │+╞══╪═╪══╡+│  ┆ ┆  │+├──┼─┼──┤+│  │ │  │+└──┴─┴──┘++Existing connections can be altered by replacing/adding characters:++>>> :{+aa2u "┌──+──┐  .─────+─────.  ┌────┐    \n\+     \│  +==+  │     |     │  │####│    \n\+     \+==+  |  │     |     │  │#   │█   \n\+     \└──+─-┘  │     +-----+  └────┘█#  \n\+     \         +=====+     │    █████#  \n\+     \╭──+─-╮  │     |     │     #####  \n\+     \│  |  |  │     |     │            \n\+     \╰──+──╯  '───────────'            "+:}+┌──┬──┐  ╭─────┬─────╮  ┌────┐+│  ╞══╡  │     │     │  │████│+╞══╡  │  │     │     │  │█   │█+└──┴──┘  │     ├─────┤  └────┘██+         ╞═════╡     │    ██████+╭──┬──╮  │     │     │     █████+│  │  │  │     │     │+╰──┴──╯  ╰───────────╯++=Limitations++Some connections do not work as expected (mostly because the corresponding+Unicode characters do not exist), e.g. rounded corners with double-stroke lines,+or connection pieces connecting horizontal single- and double-stroke lines:++>>> :{+aa2u "--+==  .==.  .--.--.   \n\+     \  |    |  |  |  |  |   \n\+     \==+--  '=='  .--+--'   \n\+     \  |          |  |  |   \n\+     \--+==  --==  '--'--'   "+:}+──┐══  ╒══╕  ╭──╮──╮+  │    │  │  │  │  │+══├──  ╘══╛  ╰──┼──╯+  │          │  │  │+──┘══  ──══  ╰──╰──╯++-}+{-# LANGUAGE DeriveFunctor   #-}+{-# LANGUAGE LambdaCase      #-}+{-# LANGUAGE RecordWildCards #-}++module Text.AsciiArt where++import           Control.Comonad+import           Data.Maybe++++--------------------------------------------------------------------------------+-- * Zipper+--------------------------------------------------------------------------------++-- | The 'Zipper' is assumed to be infinite, i.e. filled with empty values+-- outside the defined area.+data Zipper a = Zipper+    { before  :: [a]+    , current :: a+    , after   :: [a] }+    deriving (Functor)++moveBefore, moveAfter :: Zipper a -> Zipper a+moveBefore zipper@Zipper { before = a : as, current = b, after = cs }+    = zipper { before = as, current = a, after = b : cs }+moveAfter  zipper@Zipper { before = as, current = b, after = c : cs }+    = zipper { before = b : as, current = c, after = cs }++-- | Renders the 'current' and the @n - 1@ elements 'after' as list.+zipperToList :: Int -> Zipper a -> [a]+zipperToList n Zipper{..} = current : take (n - 1) after++-- | An infinite 'Zipper' filled with @a@s.+zipperOf :: a -> Zipper a+zipperOf a = Zipper { before = repeat a, current = a, after = repeat a }++-- | Takes a list and creates a 'Zipper' from it. The 'current' element will be+-- the 'head' of the list, and 'after' that 'tail'. The rest will be filled with+-- @a@s to an infinite 'Zipper'.+zipperFromList :: a -> [a] -> Zipper a+zipperFromList a = \case+    []     -> zipperOf a+    b : bs -> (zipperOf a) { current = b, after = bs ++ repeat a }+++instance Comonad Zipper where+    extract = current+    extend f zipper = fmap f $ Zipper+        { before  = iterate1 moveBefore zipper+        , current = zipper+        , after   = iterate1 moveAfter zipper }+      where+        iterate1 f x = tail (iterate f x)++++--------------------------------------------------------------------------------+-- * Plane (two-dimensional 'Zipper')+--------------------------------------------------------------------------------++-- | A plane is a 'Zipper' of 'Zipper's. The outer layer zips through lines+-- (up\/down), the inner layer through columns (left\/right).+-- Like the 'Zipper', the 'Plane' is assumed to be infinite in all directions.+newtype Plane a = Plane { unPlane :: Zipper (Zipper a) }+    deriving (Functor)++moveLeft, moveRight, moveUp, moveDown :: Plane a -> Plane a+moveLeft  = Plane . fmap moveBefore . unPlane+moveRight = Plane . fmap moveAfter  . unPlane+moveUp    = Plane . moveBefore      . unPlane+moveDown  = Plane . moveAfter       . unPlane+++-- | Renders @m@ lines and @n@ columns as nested list.+planeToList :: Int -> Int -> Plane a -> [[a]]+planeToList m n (Plane Zipper{..}) = fmap (zipperToList n) $+    current : take (m - 1) after++-- | An infinite 'Plane' filled with @a@s.+planeOf :: a -> Plane a+planeOf a = Plane $ Zipper+    { before  = repeat (zipperOf a)+    , current = zipperOf a+    , after   = repeat (zipperOf a) }++-- | Create a 'Plane' from a list of lists, filling the rest with @a@s in all+-- directions.+planeFromList :: a -> [[a]] -> Plane a+planeFromList a = \case+    []       -> planeOf a+    as : ass -> Plane $ (zipperOf (zipperOf a))+        { current = zipperFromList a as+        , after = fmap (zipperFromList a) ass ++ repeat (zipperOf a) }+++instance Comonad Plane where+    extract = current . current . unPlane+    extend f plane = fmap f $ Plane $ Zipper+        { before  = fmap foo (iterate1 moveUp plane)+        , current = foo plane+        , after   = fmap foo (iterate1 moveDown plane) }+      where+        foo p = Zipper+            { before = iterate1 moveLeft p+            , current = p+            , after = iterate1 moveRight p }+        iterate1 f x = tail (iterate f x)++++--------------------------------------------------------------------------------+-- * Patterns+--------------------------------------------------------------------------------++newtype Pattern = Pattern ((Char, Char, Char), (Char, Char, Char), (Char, Char, Char))+++patternFromString :: String -> Pattern+patternFromString [a, b, c, d, e, f, g, h, i] = Pattern ((a, b, c), (d, e, f), (g, h, i))+patternFromString _ = undefined++patternToString :: Pattern -> String+patternToString (Pattern ((a, b, c), (d, e, f), (g, h, i))) = [a, b, c, d, e, f, g, h, i]++-- | Find the 'Char' to replace the center of a 'Pattern'.+lookupPattern :: Pattern -> Maybe Char+lookupPattern pattern = case filter (satisfies pattern) patterns of+    []    -> Nothing+    a : _ -> Just (snd a)+  where+    satisfies :: Pattern -> (Pattern, Char) -> Bool+    satisfies diagram (pattern, _)+        = and (zipWith connectsLike (patternToString diagram) (patternToString pattern))++-- | Whether a character can connect to another character. For example, @+@+-- connects both horizontally (like @-@) and vertically (like @|@), so it+-- 'connectsLike' @-@, @|@, and of course like itself.+connectsLike :: Char -> Char -> Bool+char `connectsLike` pattern = case pattern of+    '-'   -> char `elem` ['-', '>', '<', '─'] || char `connectsLike` '+'+    '='   -> char `elem` ['=', '>', '<', '═'] || char `connectsLike` '+'+    '|'   -> char `elem` ['|', '^', 'v', '│'] || char `connectsLike` ':' || char `connectsLike` '+'+    ':'   -> char `elem` [':', '┆']+    '+'   -> char `elem` [ '+'+                         , '└', '┘', '┌', '┐'+                         , '╘', '╛', '╒', '╕'+                         , '├', '┤', '┬', '┴', '┼'+                         , '╞', '╡', '╤', '╧', '╪' ]+                         || char `connectsLike` '.'+    '.'   -> char `elem` [ '\'', '.'+                         , '╭', '╮', '╯', '╰' ]+    '\''  -> char `connectsLike` '.'+    ' '   -> True+    other -> char == other++-- | The actual pattern definitions. For convenience, the simple patterns are at+-- the top, and more complex ones at the bottom. 'lookupPattern' will first try+-- the most complex pattern and work its way to the simpler patterns, thus+-- avoiding to choose a simpler pattern and forgetting some connection.+patterns :: [(Pattern, Char)]+patterns = reverse $ fmap (\(a, b) -> (patternFromString a, b))+    [ ( "   \+        \ --\+        \   ", '─' )++    , ( "   \+        \-- \+        \   ", '─' )++    , ( "   \+        \ ==\+        \   ", '═' )++    , ( "   \+        \== \+        \   ", '═' )++    , ( "   \+        \ | \+        \ | ", '│' )++    , ( " | \+        \ | \+        \   ", '│' )++    , ( "   \+        \ : \+        \ | ", '┆' )++    , ( " | \+        \ : \+        \   ", '┆' )++    , ( "   \+        \ : \+        \ : ", '┆' )++    , ( " : \+        \ : \+        \   ", '┆' )++    , ( " | \+        \=+ \+        \   ", '╛' )++    , ( " | \+        \ +=\+        \   ", '╘' )++    , ( "   \+        \ +=\+        \ | ", '╒' )++    , ( "   \+        \=+ \+        \ | ", '╕' )++    , ( " | \+        \ +=\+        \ | ", '╞' )++    , ( " | \+        \=+ \+        \ | ", '╡' )++    , ( "   \+        \=+=\+        \ | ", '╤' )++    , ( " | \+        \=+=\+        \   ", '╧' )++    , ( " | \+        \=+=\+        \ | ", '╪' )++    , ( " | \+        \-+ \+        \   ", '┘' )++    , ( " | \+        \ +-\+        \   ", '└' )++    , ( "   \+        \ +-\+        \ | ", '┌' )++    , ( "   \+        \-+ \+        \ | ", '┐' )++    , ( " | \+        \ +-\+        \ | ", '├' )++    , ( " | \+        \-+ \+        \ | ", '┤' )++    , ( "   \+        \-+-\+        \ | ", '┬' )++    , ( " | \+        \-+-\+        \   ", '┴' )++    , ( " | \+        \-+-\+        \ | ", '┼' )++    , ( "   \+        \ .-\+        \ | ", '╭' )++    , ( "   \+        \-. \+        \ | ", '╮' )++    , ( " | \+        \-' \+        \   ", '╯' )++    , ( " | \+        \ '-\+        \   ", '╰' )++    , ( "   \+        \ # \+        \ # ", '█' )++    , ( "   \+        \## \+        \   ", '█' )++    , ( "   \+        \ ##\+        \   ", '█' )++    , ( " # \+        \ # \+        \   ", '█' )++    , ( "   \+        \-> \+        \   ", '▷' )++    , ( "   \+        \ <-\+        \   ", '◁' )++    , ( "   \+        \ ^ \+        \ | ", '△' )++    , ( " | \+        \ v \+        \   ", '▽' )+    ]++++--------------------------------------------------------------------------------+-- * Transforming ASCII to Unicode+--------------------------------------------------------------------------------++-- | Match the 'current' element and its eight neighbours against the defined+-- 'patterns' and choose the 'Char' from the matching 'Pattern'.+substituteChar :: Plane Char -> Char+substituteChar = \case+    Plane ( Zipper ((Zipper (a : as) b (c : cs)) : _)+                   ( Zipper (d : ds) e (f : fs))+                   ((Zipper (g : gs) h (i : is)) : _) )+        -> fromMaybe e (lookupPattern (patternFromString [a, b, c, d, e, f, g, h, i]))+    _ -> undefined -- We assume an infinite Zipper!++-- | Transform a 'Plane' of ASCII characters to an equivalent plane where the+-- ASCII box drawings have been replaced by their Unicode counterpart.+--+-- This function is a convolution with 'substituteChar' using the 'Comonad'ic+-- 'extend'.+renderAsciiToUnicode :: Plane Char -> Plane Char+renderAsciiToUnicode = extend substituteChar++++{- $setup+>>> :{+aa2u :: String -> IO ()+aa2u input =+    let inputLines = lines input+        plane = planeFromList ' ' inputLines+        width = maximum (fmap length inputLines)+        height = length inputLines+    in  putStr+        . unlines+        . fmap (reverse . dropWhile (== ' ') . reverse)+        . planeToList height width+        . renderAsciiToUnicode+        $ plane+:}+-}
+ stack.yaml view
@@ -0,0 +1,66 @@+# This file was automatically generated by 'stack init'+#+# Some commonly used options have been documented as comments in this file.+# For advanced use and comprehensive documentation of the format, please see:+# http://docs.haskellstack.org/en/stable/yaml_configuration/++# Resolver to choose a 'specific' stackage snapshot or a compiler version.+# A snapshot resolver dictates the compiler version and the set of packages+# to be used for project dependencies. For example:+#+# resolver: lts-3.5+# resolver: nightly-2015-09-21+# resolver: ghc-7.10.2+# resolver: ghcjs-0.1.0_ghc-7.10.2+# resolver:+#  name: custom-snapshot+#  location: "./custom-snapshot.yaml"+resolver: lts-8.5++# User packages to be built.+# Various formats can be used as shown in the example below.+#+# packages:+# - some-directory+# - https://example.com/foo/bar/baz-0.0.2.tar.gz+# - location:+#    git: https://github.com/commercialhaskell/stack.git+#    commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a+# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a+#   extra-dep: true+#  subdirs:+#  - auto-update+#  - wai+#+# A package marked 'extra-dep: true' will only be built if demanded by a+# non-dependency (i.e. a user package), and its test suites and benchmarks+# will not be run. This is useful for tweaking upstream packages.+packages:+- '.'+# Dependency packages to be pulled from upstream that are not in the resolver+# (e.g., acme-missiles-0.3)+extra-deps: []++# Override default flag values for local packages and extra-deps+flags: {}++# Extra package databases containing global packages+extra-package-dbs: []++# Control whether we use the GHC we find on the path+# system-ghc: true+#+# Require a specific version of stack, using version ranges+# require-stack-version: -any # Default+# require-stack-version: ">=1.3"+#+# Override the architecture used by stack, especially useful on Windows+# arch: i386+# arch: x86_64+#+# Extra directories used by stack for building+# extra-include-dirs: [/path/to/dir]+# extra-lib-dirs: [/path/to/dir]+#+# Allow a newer minor version of GHC than the snapshot specifies+# compiler-check: newer-minor
+ test/Spec.hs view
@@ -0,0 +1,12 @@+import Test.DocTest++main :: IO ()+main =+    let extensions =+            [ "-XLambdaCase"+            , "-XMultiWayIf"+            , "-XOverloadedStrings" ]+        sourceFolders =+            [ "src"+            , "app" ]+    in  doctest (extensions ++ sourceFolders)