mida 1.0.1 → 1.0.2
raw patch · 16 files changed
+194/−191 lines, 16 filesdep +file-embeddep +hspecdep +semigroupsdep −test-frameworkdep −test-framework-quickcheck2dep ~HCodecsdep ~QuickCheckdep ~aeson
Dependencies added: file-embed, hspec, semigroups
Dependencies removed: test-framework, test-framework-quickcheck2
Dependency ranges changed: HCodecs, QuickCheck, aeson, base, containers, data-default, exceptions, filepath, formatting, haskeline, megaparsec, mida, mtl, optparse-applicative, path, path-io, process, text, tf-random, transformers, yaml
Files
- CHANGELOG.md +4/−0
- license.txt +16/−0
- mida.cabal +55/−49
- notice.txt +5/−0
- src/Main.hs +5/−21
- src/Mida/Interaction.hs +1/−1
- src/Mida/Interaction/Commands.hs +4/−4
- src/Mida/Language.hs +2/−1
- src/Mida/Language/Element.hs +2/−1
- src/Mida/Language/Environment.hs +4/−3
- src/Mida/Language/Eval.hs +13/−11
- src/Mida/Language/SyntaxTree.hs +57/−3
- src/Mida/Representation.hs +1/−2
- src/Mida/Representation/Parser.hs +8/−15
- src/Mida/Representation/Show.hs +4/−4
- tests/Main.hs +13/−76
CHANGELOG.md view
@@ -1,3 +1,7 @@+## MIDA 1.0.2++* Refactoring, switch to Hspec for testing and Megaparsec 5.+ ## MIDA 1.0.1 * Fixed issues with `directory` package. Please use `directory-1.2.3.0` or
+ license.txt view
@@ -0,0 +1,16 @@+MIDA — realization of MIDA, language for generation of MIDI files.++Copyright © 2014–2016 Mark Karpov++MIDA is free software: you can redistribute it and/or modify it under the+terms of the GNU General Public License as published by the Free Software+Foundation, either version 3 of the License, or (at your option) any+later version.++MIDA is distributed in the hope that it will be useful, but WITHOUT ANY+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more+details.++You should have received a copy of the GNU General Public License along+with this program. If not, see <http://www.gnu.org/licenses/>.
mida.cabal view
@@ -17,7 +17,8 @@ -- with this program. If not, see <http://www.gnu.org/licenses/>. name: mida-version: 1.0.1+version: 1.0.2+cabal-version: >= 1.10 synopsis: Language for algorithmic generation of MIDI files description: @@ -50,8 +51,10 @@ copyright: Copyright © 2014–2016 Mark Karpov category: Language build-type: Simple-extra-source-files: README.md, CHANGELOG.md-cabal-version: >= 1.10+data-files: notice.txt+ , license.txt+extra-doc-files: CHANGELOG.md+ , README.md flag dev description: Turn on development settings.@@ -64,18 +67,20 @@ ghc-options: -O2 -Wall -Werror else ghc-options: -O2 -Wall- ghc-prof-options: -O2 -Wall -prof -fprof-cafs -rtsopts- build-depends: HCodecs >= 0.5- , base >= 4.8 && < 5- , containers >= 0.5.5.1- , exceptions >= 0.8- , haskeline >= 0.7.1.3- , megaparsec >= 4.2- , mtl >= 2.1.3.1+ build-depends: HCodecs >= 0.5 && < 0.6+ , QuickCheck >= 2.8.2 && < 3.0+ , base >= 4.8 && < 5.0+ , containers >= 0.5.5.1 && < 0.6+ , exceptions >= 0.8 && < 0.9+ , haskeline >= 0.7.1.3 && < 0.8+ , megaparsec >= 5.0 && < 6.0+ , mtl >= 2.1.3.1 && < 3.0 , random- , text >= 1.2.0.4- , tf-random >= 0.5- , transformers >= 0.2.0.0+ , text >= 1.2.0.4 && < 1.3+ , tf-random >= 0.5 && < 1.0+ , transformers >= 0.2.0.0 && < 0.6+ if !impl(ghc >= 8.0)+ build-depends: semigroups == 0.18.* default-extensions: FlexibleContexts , FlexibleInstances , OverloadedStrings@@ -95,33 +100,35 @@ executable mida main-is: Main.hs hs-source-dirs: src- other-modules: Mida.Interaction if flag(dev) ghc-options: -O2 -Wall -Werror else ghc-options: -O2 -Wall- ghc-prof-options: -O2 -Wall -prof -fprof-cafs -rtsopts- build-depends: HCodecs >= 0.5- , aeson >= 0.7- , base >= 4.8 && < 5- , containers >= 0.5.5.1- , data-default >= 0.5.3- , exceptions >= 0.8- , filepath >= 1.3.0.2- , formatting >= 6.2- , haskeline >= 0.7.1.3- , megaparsec >= 4.2- , mida >= 1.0.1- , mtl >= 2.1.3.1- , optparse-applicative >= 0.11.0.2- , path >= 0.5.3- , path-io >= 0.3.1- , process >= 1.2.0.0+ build-depends: HCodecs >= 0.5 && < 0.6+ , QuickCheck >= 2.8.2 && < 3.0+ , aeson >= 0.7 && < 0.12+ , base >= 4.8 && < 5.0+ , containers >= 0.5.5.1 && < 0.6+ , data-default >= 0.5.3 && < 0.8+ , exceptions >= 0.8 && < 0.9+ , file-embed >= 0.0.9+ , filepath >= 1.3.0.2 && < 1.5+ , formatting >= 6.2 && < 7.0+ , haskeline >= 0.7.1.3 && < 0.8+ , megaparsec >= 5.0 && < 6.0+ , mida >= 1.0.2+ , mtl >= 2.1.3.1 && < 3.0+ , optparse-applicative >= 0.11.0.2 && < 0.13+ , path >= 0.5.3 && < 0.6+ , path-io >= 0.3.1 && < 2.0+ , process >= 1.2 && < 1.5 , random- , text >= 1.2.0.4- , tf-random >= 0.5- , transformers >= 0.2.0.0- , yaml >= 0.8.15.1+ , text >= 1.2.0.4 && < 1.3+ , tf-random >= 0.5 && < 1.0+ , transformers >= 0.2.0.0 && < 0.6+ , yaml >= 0.8.15 && < 0.9+ if !impl(ghc >= 8.0)+ build-depends: semigroups == 0.18.* default-extensions: FlexibleContexts , FlexibleInstances , OverloadedStrings@@ -153,20 +160,19 @@ else ghc-options: -O2 -Wall default-language: Haskell2010- build-depends: HCodecs >= 0.5- , QuickCheck >= 2.4 && < 3- , base >= 4.8 && < 5- , containers >= 0.5.5.1- , megaparsec >= 4.2- , mida >= 1.0.1- , mtl >= 2.1.3.1+ build-depends: HCodecs >= 0.5 && < 0.6+ , QuickCheck >= 2.8.2 && < 3.0+ , base >= 4.8 && < 5.0+ , containers >= 0.5.5.1 && < 0.6+ , hspec >= 2.0 && < 3.0+ , megaparsec >= 5.0 && < 6.0+ , mida >= 1.0.2+ , mtl >= 2.1.3.1 && < 3.0 , random- , test-framework >= 0.4 && < 1- , test-framework-quickcheck2 >= 0.3 && < 0.4- , text >= 1.2.0.4- , tf-random >= 0.5- , transformers >= 0.2.0.0+ , text >= 1.2.0.4 && < 1.3+ , tf-random >= 0.5 && < 1.0+ , transformers >= 0.2.0.0 && < 0.6 source-repository head type: git- location: git://github.com/mrkkrp/mida.git+ location: https://github.com/mrkkrp/mida.git
+ notice.txt view
@@ -0,0 +1,5 @@+MIDA Copyright © 2014–2016 Mark Karpov++This program comes with ABSOLUTELY NO WARRANTY. This is free software,+and you are welcome to redistribute it under certain conditions; see+GNU General Public License for details.
src/Main.hs view
@@ -16,10 +16,13 @@ -- You should have received a copy of the GNU General Public License along -- with this program. If not, see <http://www.gnu.org/licenses/>. +{-# LANGUAGE TemplateHaskell #-}+ module Main (main) where import Control.Monad import Control.Monad.IO.Class+import Data.FileEmbed import Data.Text.Lazy (Text) import Data.Version (showVersion) import Formatting@@ -64,31 +67,12 @@ -- | Shortish copyright notice. notice :: Text-notice =- "MIDA Copyright © 2014–2016 Mark Karpov\n\n\- \This program comes with ABSOLUTELY NO WARRANTY. This is free software,\n\- \and you are welcome to redistribute it under certain conditions; see\n\- \GNU General Public License for details.\n"+notice = $(embedStringFile "notice.txt") -- | Longer copyright notice. license :: Text-license =- "MIDA — realization of MIDA, language for generation of MIDI files.\n\- \Copyright © 2014–2016 Mark Karpov\n\- \\n\- \MIDA is free software: you can redistribute it and/or modify it under the\n\- \terms of the GNU General Public License as published by the Free Software\n\- \Foundation, either version 3 of the License, or (at your option) any\n\- \later version.\n\- \\n\- \MIDA is distributed in the hope that it will be useful, but WITHOUT ANY\n\- \WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n\- \FOR A PARTICULAR PURPOSE. See the GNU General Public License for more\n\- \details.\n\- \\n\- \You should have received a copy of the GNU General Public License along\n\- \with this program. If not, see <http://www.gnu.org/licenses/>.\n"+license = $(embedStringFile "license.txt") -- | Read configuration file if present and run MIDA monad.
src/Mida/Interaction.hs view
@@ -90,7 +90,7 @@ file <- gets stSrcFile case parseMida (fromAbsFile file) expr of Right x -> mapM_ f x- Left x -> liftIO $ fprint (string % "\n") x+ Left x -> liftIO (putStr x) where f (Definition n t) = processDef n t f (Exposition t) = do len <- gets stPrevLen
src/Mida/Interaction/Commands.hs view
@@ -50,11 +50,11 @@ , createProcess , waitForProcess , delegate_ctlc )-import qualified Codec.Midi as Midi-import qualified Data.Text.Lazy as T-import qualified Data.Text.Lazy.IO as T+import qualified Codec.Midi as Midi+import qualified Data.Text.Lazy as T+import qualified Data.Text.Lazy.IO as T import qualified System.Console.Haskeline as L-import qualified System.FilePath as FP+import qualified System.FilePath as FP -- | Description of REPL command.
src/Mida/Language.hs view
@@ -19,6 +19,7 @@ module Mida.Language ( SyntaxTree , Sel (..)+ , Statement (..) , Principle , Element (..) , MidaEnv@@ -38,7 +39,7 @@ , toPrin ) where -import Mida.Language.SyntaxTree (SyntaxTree, Sel (..))+import Mida.Language.SyntaxTree (SyntaxTree, Sel (..), Statement (..)) import Mida.Language.Element (Principle, Element (..)) import Mida.Language.Environment import Mida.Language.Eval (evalDef, eval, toPrin)
src/Mida/Language/Element.hs view
@@ -26,6 +26,7 @@ where import Control.Arrow ((***))+import Data.List.NonEmpty (NonEmpty (..)) import Numeric.Natural -- | Collection of elements for evaluation, representation of some aspect of@@ -39,7 +40,7 @@ = Val a -- ^ Single value, evaluates to itself | Sec [Element a] -- ^ Universal container for other values | Mul [Element a] -- ^ Multivalue, the way to introduce varying elements- | CMul [([Element a], [Element a])] -- ^ Conditional multivalue+ | CMul (NonEmpty ([Element a], [Element a])) -- ^ Conditional multivalue deriving (Eq, Show, Functor, Foldable) instance Applicative Element where
src/Mida/Language/Environment.hs view
@@ -48,10 +48,11 @@ import Mida.Representation.Base (noteAlias, modifiers) import Mida.Representation.Show (showDefinition) import Numeric.Natural-import System.Console.Haskeline.MonadException -- FIXME+import System.Console.Haskeline.MonadException import System.Random (split) import System.Random.TF (TFGen, mkTFGen)-import qualified Data.Map.Strict as M+import qualified Data.List.NonEmpty as NE+import qualified Data.Map.Strict as M -- | MIDA environment state. Basically this amounts to collection of -- definitions and random number generator.@@ -184,7 +185,7 @@ f (Value _) = mempty f (Section x) = cm x f (Multi x) = cm x- f (CMulti x) = x >>= (cm *** cm >>> uncurry (<>))+ f (CMulti x) = NE.toList x >>= (cm *** cm >>> uncurry (<>)) f (Reference x) = return x <> tDefs x defs f (Range _ _) = mempty f (Product x y) = f x <> f y
src/Mida/Language/Eval.hs view
@@ -29,6 +29,7 @@ import Control.Monad.State.Class import Control.Monad.State.Lazy import Data.List (tails)+import Data.List.NonEmpty (NonEmpty (..)) import Data.Maybe (listToMaybe) import Data.Monoid ((<>)) import Mida.Language.Element@@ -37,6 +38,7 @@ import Numeric.Natural import System.Random (next) import System.Random.TF (TFGen)+import qualified Data.List.NonEmpty as NE -- | State record used for calculation\/evaluation of principles. @@ -70,8 +72,8 @@ where f (Val x) = addHistory x >> return [x] f (Sec x) = resolve x f (Mul x) = choice x >>= maybe (return []) f- f (CMul x) = listToMaybe <$> filterM (matchHistory . fst) x >>=- maybe (f . toMul $ x) (f . Mul . snd)+ f (CMul x) = listToMaybe <$> filterM (matchHistory . fst) (NE.toList x)+ >>= maybe (f . toMul $ x) (f . Mul . snd) -- | Run lazy state monad with 'CalcSt' state. @@ -112,9 +114,9 @@ -- | Convert internals of conditional multivalue into plain multivalue. toMul- :: [([Element Natural], [Element Natural])] -- ^ Pattern\/result pairs+ :: NonEmpty ([Element Natural], [Element Natural]) -- ^ Pattern\/result pairs -> Element Natural -- ^ Internals of plain multivalue-toMul xs = Mul (xs >>= snd)+toMul xs = Mul (NE.toList xs >>= snd) -- | A monadic wrapper around 'condMatch'. @@ -155,13 +157,13 @@ -- several elements after simplification. simplifyElt :: Element Natural -> Principle-simplifyElt x@(Val _) = [x]-simplifyElt (Sec [x]) = simplify [x]-simplifyElt (Mul [x]) = simplify [x]-simplifyElt (CMul [(_, xs)]) = simplifyElt (Mul xs)-simplifyElt (Sec xs) = [Sec (simplifySec xs)]-simplifyElt (Mul xs) = [Mul (simplify xs)]-simplifyElt (CMul xs) = [CMul ((simplify *** simplify) <$> xs)]+simplifyElt x@(Val _) = [x]+simplifyElt (Sec [x]) = simplify [x]+simplifyElt (Mul [x]) = simplify [x]+simplifyElt (CMul ((_, xs):|[])) = simplifyElt (Mul xs)+simplifyElt (Sec xs) = [Sec (simplifySec xs)]+simplifyElt (Mul xs) = [Mul (simplify xs)]+simplifyElt (CMul xs) = [CMul ((simplify *** simplify) <$> xs)] -- | The meat of the algorithm that transforms 'SyntaxTree' into 'Principle'.
src/Mida/Language/SyntaxTree.hs view
@@ -18,16 +18,20 @@ module Mida.Language.SyntaxTree ( SyntaxTree- , Sel (..) )+ , Sel (..)+ , Statement (..) ) where +import Data.Char (isLetter, isAlphaNum)+import Data.List.NonEmpty (NonEmpty (..)) import Numeric.Natural+import Test.QuickCheck -- | Syntax tree in our case is just a collection of syntactic elements. type SyntaxTree = [Sel] --- | Syntactic element corresponds to language features. Some of them have+-- | Syntactic element corresponds to language tokens. Some of them have -- direct corresponding constructor in 'Mida.Language.Element.Element', -- others have to be simplified first. @@ -35,7 +39,7 @@ = Value Natural -- ^ Literal value | Section [Sel] -- ^ Section | Multi [Sel] -- ^ Multivalue- | CMulti [([Sel], [Sel])] -- ^ Conditional multivalue+ | CMulti (NonEmpty ([Sel], [Sel])) -- ^ Conditional multivalue | Reference String -- ^ Reference (name of variable) | Range Natural Natural -- ^ Range of values | Product Sel Sel -- ^ Product of principles@@ -46,3 +50,53 @@ | Rotation Sel Sel -- ^ Rotation | Reverse Sel -- ^ Reversed principle deriving (Eq, Show)++instance Arbitrary Sel where+ arbitrary = sized arbitrarySel++arbitrarySel :: Int -> Gen Sel+arbitrarySel 0 =+ oneof [ Value <$> nonNegative+ , Reference <$> identifier+ , Range <$> nonNegative <*> nonNegative ]+ where nonNegative = getNonNegative <$> arbitrary+arbitrarySel n =+ oneof [ Section <$> listSel+ , Multi <$> listSel+ , CMulti <$> listCnd+ , Product <$> leafSel <*> leafSel+ , Division <$> leafSel <*> leafSel+ , Sum <$> leafSel <*> leafSel+ , Diff <$> leafSel <*> leafSel+ , Loop <$> leafSel <*> leafSel+ , Rotation <$> leafSel <*> leafSel+ , Reverse <$> leafSel ]+ where cnSel d = arbitrarySel (n `div` d)+ vcSel d = listOf (cnSel $ d * 20)+ leafSel = cnSel 10+ listSel = vcSel 20+ listCnd =+ let x = (,) <$> vcSel 25 <*> vcSel 25+ in (:|) <$> x <*> listOf x++-- | Statement can be either definition or exposition. Expositions are only+-- used in REPL.++data Statement+ = Definition String SyntaxTree+ | Exposition SyntaxTree+ deriving (Eq, Show)++instance Arbitrary Statement where+ arbitrary =+ oneof [ Definition <$> identifier <*> arbitrary+ , Exposition <$> arbitrary ]++----------------------------------------------------------------------------+-- Helpers++identifier :: Gen String+identifier = (:) <$> ch0 <*> chN+ where ch0 = u isLetter+ chN = listOf $ u isAlphaNum+ u f = frequency [(1, return '_'), (74, arbitrary `suchThat` f)]
src/Mida/Representation.hs view
@@ -18,8 +18,7 @@ -- with this program. If not, see <http://www.gnu.org/licenses/>. module Mida.Representation- ( Statement (..)- , probeMida+ ( probeMida , parseMida , showStatement , showSyntaxTree
src/Mida/Representation/Parser.hs view
@@ -18,30 +18,22 @@ -- with this program. If not, see <http://www.gnu.org/licenses/>. module Mida.Representation.Parser- ( Statement (..)- , probeMida+ ( probeMida , parseMida ) where import Control.Applicative (empty) import Control.Monad (void) import Data.Text.Lazy (Text)-import Mida.Language.SyntaxTree (SyntaxTree, Sel (..))+import Mida.Language.SyntaxTree import Numeric.Natural import Text.Megaparsec import Text.Megaparsec.Expr import Text.Megaparsec.Text.Lazy-import qualified Data.Text.Lazy as T+import qualified Data.List.NonEmpty as NE+import qualified Data.Text.Lazy as T import qualified Mida.Representation.Base as B-import qualified Text.Megaparsec.Lexer as L---- | Statement can be either definition or exposition. Expositions are only--- used in REPL.--data Statement- = Definition String SyntaxTree- | Exposition SyntaxTree- deriving (Eq, Show)+import qualified Text.Megaparsec.Lexer as L -- | Test if given fragment of MIDA code is finished and self-contained. @@ -67,7 +59,7 @@ Right x -> if null x then Left ('\"' : file ++ "\":\ninvalid definition syntax") else Right x- Left e -> Left (show e)+ Left e -> Left (parseErrorPretty e) where parser = if T.pack B.defOp `T.isInfixOf` txt then pSource else pure <$> pExposition@@ -121,7 +113,8 @@ pMulti = Multi <$> braces pPrinciple pCMulti :: Parser Sel-pCMulti = CMulti <$> braces (many $ (,) <$> angles pPrinciple <*> pPrinciple)+pCMulti = CMulti . NE.fromList <$>+ braces (some $ (,) <$> angles pPrinciple <*> pPrinciple) pExpression :: Parser Sel pExpression = makeExprParser (parens pExpression <|> pElement) optTable
src/Mida/Representation/Show.hs view
@@ -30,10 +30,10 @@ import Data.Text.Lazy (Text) import Mida.Language.Element import Mida.Language.SyntaxTree-import Mida.Representation.Parser (Statement (..))-import qualified Data.Text.Lazy.Builder as T (Builder, fromString, toLazyText)+import qualified Data.List.NonEmpty as NE+import qualified Data.Text.Lazy.Builder as T (Builder, fromString, toLazyText) import qualified Data.Text.Lazy.Builder.Int as T (decimal)-import qualified Mida.Representation.Base as B+import qualified Mida.Representation.Base as B -- | Render a statement. This handles definitions and expositions. @@ -86,7 +86,7 @@ f (Value x) = T.decimal x f (Section x) = "[" <> cm f x <> "]" f (Multi x) = "{" <> cm f x <> "}"- f (CMulti x) = "{" <> cm (c *** cm f >>> uncurry (<>)) x <> "}"+ f (CMulti x) = "{" <> cm (c *** cm f >>> uncurry (<>)) (NE.toList x) <> "}" f (Reference x) = T.fromString x f (Range x y) = T.decimal x <> T.fromString B.rangeOp <> T.decimal y f (Product x y) = f x <> pad B.productOp <> p y
tests/Main.hs view
@@ -1,5 +1,5 @@ ----- QuickCheck tests for MIDA.+-- Test suite of MIDA. -- -- Copyright © 2014–2016 Mark Karpov --@@ -16,84 +16,21 @@ -- You should have received a copy of the GNU General Public License along -- with this program. If not, see <http://www.gnu.org/licenses/>. -{-- TODO:- - parser for configuration files;- - translation from syntax tree to internal language;- - process of translation Principle -> [Int];- - generation of MIDI files.--}--{-# OPTIONS -fno-warn-orphans #-}- module Main (main) where -import Control.Arrow ((&&&), (>>>))-import Data.Char (isLetter, isAlphaNum)--import Test.Framework (defaultMain, testGroup, Test)-import Test.Framework.Providers.QuickCheck2 (testProperty)-import Test.QuickCheck--import Mida.Language (Sel (..)) import Mida.Representation- ( Statement (..)- , probeMida- , parseMida- , showStatement )+import Test.Hspec+import Test.QuickCheck main :: IO ()-main = defaultMain tests--tests :: [Test]-tests =- [ testGroup "Printer and Parser"- [ testProperty "Valid MIDA Probe" prop_valid_probe- , testProperty "Printer and Parser Consistency" prop_pp_consistency ] ]--prop_valid_probe :: Statement -> Bool-prop_valid_probe = probeMida . showStatement--prop_pp_consistency :: Statement -> Bool-prop_pp_consistency = id &&& (parseMida "" . showStatement) >>> check- where check (x, Right [y]) = x == y- check _ = False--instance Arbitrary Statement where- arbitrary =- oneof [ Definition <$> identifier <*> arbitrary- , Exposition <$> arbitrary ]--instance Arbitrary Sel where- arbitrary = sized arbitrarySel--arbitrarySel :: Int -> Gen Sel-arbitrarySel 0 =- oneof [ Value <$> nonNegative- , Reference <$> identifier- , Range <$> nonNegative <*> nonNegative ]- where nonNegative = getNonNegative <$> arbitrary-arbitrarySel n =- oneof [ Section <$> listSel- , Multi <$> listSel- , CMulti <$> listCnd- , Product <$> leafSel <*> leafSel- , Division <$> leafSel <*> leafSel- , Sum <$> leafSel <*> leafSel- , Diff <$> leafSel <*> leafSel- , Loop <$> leafSel <*> leafSel- , Rotation <$> leafSel <*> leafSel- , Reverse <$> leafSel ]- where cnSel d = arbitrarySel (n `div` d)- vcSel d = (getNonNegative <$> arbitrary)- >>= \s -> vectorOf s (cnSel $ d * s)- leafSel = cnSel 2- listSel = vcSel 1- listCnd = (getPositive <$> arbitrary)- >>= \s -> vectorOf s $ (,) <$> vcSel s <*> vcSel s+main = hspec spec -identifier :: Gen String-identifier = (:) <$> ch0 <*> chN- where ch0 = u isLetter- chN = listOf $ u isAlphaNum- u f = frequency [(1, return '_'), (74, arbitrary `suchThat` f)]+spec :: Spec+spec = do+ describe "probeMida" $+ it "returns True for every correct statement" $+ property (probeMida . showStatement)+ describe "parser and printer" $+ it "printed representation of statement can be parsed back" $+ property $ \stmt ->+ parseMida "" (showStatement stmt) `shouldBe` Right [stmt]